jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

SVG from Zero to Senior · Part 3 — Painting: Fill, Stroke, Gradients & Patterns

Make SVG beautiful: fill and the two fill rules, the whole stroke family (width, linecap, linejoin, dasharray), then reusable linear/radial gradients and tiling patterns defined once in defs. With a live paint playground.

You can now describe any shape (Part 1) and any outline (Part 2). {Giờ con tả được mọi hình (Phần 1) và mọi đường viền (Phần 2).} But a shape with no paint is invisible — time to open the paint box. {Nhưng hình không sơn thì vô hình — đến lúc mở hộp màu.} In SVG, painting splits into two jobs: fill (the inside) and stroke (the outline), and each is far richer than CSS background/border. {Trong SVG, việc sơn chia làm hai: fill (bên trong) và stroke (đường viền), và mỗi cái phong phú hơn background/border của CSS nhiều.}

Tweak the playground — switch the fill source, drag the stroke sliders — then read how each piece works. {Chỉnh playground — đổi nguồn fill, kéo slider stroke — rồi mới đọc cách từng phần hoạt động.}

Open the full demo {Mở demo đầy đủ}: /tools/svg-paint-demo/.

Presentation attributes vs CSS {Thuộc tính trình bày vs CSS}

First, a senior-level fact that saves hours of confusion. {Đầu tiên, một sự thật tầm senior giúp tiết kiệm hàng giờ bối rối.} Paint can be set two ways: {Sơn có thể đặt theo hai cách:}

<!-- presentation attribute -->
<circle fill="red" stroke="black" />

<!-- ...or CSS, which wins over the attribute -->
<circle style="fill: red; stroke: black" />

Presentation attributes act like very weak CSS — any real CSS rule (even a class) overrides them. {Thuộc tính trình bày hoạt động như CSS rất yếu — bất kỳ luật CSS thật nào (kể cả class) cũng ghi đè chúng.} That is why you can theme an icon with .icon { fill: currentColor } even when the SVG file has fill="#000" baked in. {Đó là lý do con có thể đổi màu icon bằng .icon { fill: currentColor } dù file SVG đã nhúng sẵn fill="#000".} We exploit this hard in Part 10. {Ta khai thác mạnh điều này ở Phần 10.}

fill and the two fill rules {fill và hai fill rule}

fill takes any CSS color, none, currentColor, or a url(#id) reference. {fill nhận mọi màu CSS, none, currentColor, hoặc tham chiếu url(#id).} The subtle part is how the inside is decided for self-intersecting shapes, via fill-rule: {Phần tinh tế là bên trong được quyết định thế nào cho các hình tự cắt, qua fill-rule:}

  • nonzero (default) — counts winding direction; almost always what you want. {đếm hướng cuốn; gần như luôn là cái con muốn.}
  • evenodd — fills based on how many edges a ray crosses; gives “donut hole” stars and overlaps. {tô dựa trên số cạnh một tia cắt qua; cho ra sao kiểu “lỗ bánh donut” và phần chồng.}
<path d="…star…" fill-rule="evenodd" />

Most of the time you ignore this — until a filled star looks solid when you wanted a hole. Now you know the switch. {Phần lớn thời gian con bỏ qua nó — cho tới khi một ngôi sao tô trông đặc dù con muốn có lỗ. Giờ con biết cái công tắc.}

The stroke family {Họ stroke}

stroke is where SVG outshines CSS borders, because the outline follows any path, not just a box. {stroke là nơi SVG vượt border của CSS, vì đường viền đi theo mọi path, không chỉ cái hộp.}

<path d="…"
  stroke="#60a5fa"
  stroke-width="6"
  stroke-linecap="round"     /* butt | round | square the END of an open line */
  stroke-linejoin="round"    /* miter | round | bevel where segments meet */
  stroke-dasharray="12 6"    /* 12 on, 6 off, repeating */
  stroke-dashoffset="0"      /* shift the dash pattern the key to line-drawing */
/>

A few things beginners miss: {Vài điều người mới bỏ lỡ:}

  • The stroke is centred on the path — half spills inside, half outside. {Stroke nằm giữa đường — nửa tràn vào trong, nửa ra ngoài.}
  • stroke-linecap only affects the ends of open subpaths; closed shapes use linejoin everywhere. {stroke-linecap chỉ ảnh hưởng đầu của subpath hở; hình kín dùng linejoin khắp nơi.}
  • stroke-dasharray + stroke-dashoffset is the trick behind the “drawing itself” animation we build in Part 6. {stroke-dasharray + stroke-dashoffset là mánh đằng sau animation “tự vẽ” ta dựng ở Phần 6.} Remember the pair — it pays off later. {Nhớ cặp đôi này — nó có ích về sau.}

defs + url(#id): define once, paint many {defs + url(#id): định nghĩa một lần, sơn nhiều lần}

Gradients and patterns are not colors — they are paint servers you define inside <defs> and reference by id. {Gradient và pattern không phải màu — chúng là paint server con định nghĩa trong <defs> và tham chiếu bằng id.} <defs> holds things that are not drawn directly, only referenced. {<defs> chứa những thứ không vẽ trực tiếp, chỉ để tham chiếu.}

Linear gradient {Gradient tuyến tính}

<defs>
  <linearGradient id="brand" x1="0" y1="0" x2="1" y2="1">
    <stop offset="0%"  stop-color="#c8ff00" />
    <stop offset="100%" stop-color="#60a5fa" />
  </linearGradient>
</defs>
<rect width="100" height="100" fill="url(#brand)" />

x1,y1 → x2,y2 is the gradient’s direction. {x1,y1 → x2,y2 là hướng của gradient.} By default coordinates are objectBoundingBox (0→1 across the shape); switch to gradientUnits="userSpaceOnUse" to use real user coordinates. {Mặc định toạ độ là objectBoundingBox (0→1 trên hình); chuyển gradientUnits="userSpaceOnUse" để dùng toạ độ user thật.}

Radial gradient {Gradient toả tròn}

<radialGradient id="sun" cx="50%" cy="40%" r="60%">
  <stop offset="0%"  stop-color="#f59e0b" />
  <stop offset="100%" stop-color="#ef4444" />
</radialGradient>

cx,cy,r set the circle; add fx,fy to move the highlight off-centre for a 3D sphere look. {cx,cy,r đặt vòng tròn; thêm fx,fy để dời điểm sáng lệch tâm cho hiệu ứng quả cầu 3D.}

Pattern {Pattern}

<pattern id="dots" width="20" height="20" patternUnits="userSpaceOnUse">
  <circle cx="10" cy="10" r="4" fill="#34d399" />
</pattern>
<rect width="200" height="200" fill="url(#dots)" />

A pattern tiles a small drawing across the fill — dots, hatching, even a texture image. {Pattern lát một bản vẽ nhỏ khắp vùng fill — chấm, gạch chéo, thậm chí ảnh texture.} The same gradient or pattern id can paint hundreds of shapes; it is defined exactly once. {Cùng một id gradient/pattern sơn được hàng trăm hình; nó chỉ được định nghĩa đúng một lần.}

The master’s warnings {Lời cảnh báo của sư phụ}

  • fill defaults to black, not transparent. A shape with no fill attribute is solid black, not invisible. Set fill="none" for outline-only. {fill mặc định đen, không trong suốt. Hình không có thuộc tính fill là đen đặc, không phải vô hình. Đặt fill="none" nếu chỉ muốn viền.}
  • CSS beats presentation attributes. If fill="red" “isn’t working,” a CSS rule is overriding it. {CSS thắng thuộc tính trình bày. Nếu fill="red" “không ăn”, có một luật CSS đang ghi đè.}
  • Ids must be unique on the page. Two <linearGradient id="g"> and the second is ignored — a classic bug when inlining many SVGs. {Id phải duy nhất trên trang. Hai id="g" thì cái thứ hai bị bỏ qua — lỗi kinh điển khi inline nhiều SVG.}

Practice, or it didn’t happen {Luyện tập, không thì coi như chưa học}

  1. Brand badge {Huy hiệu thương hiệu}: a rounded rect filled with a 2-stop linear gradient and a 2px stroke. {rect bo góc tô gradient tuyến tính 2 stop và stroke 2px.}
  2. Dashed ring {Vòng nét đứt}: a <circle> with fill="none", a thick stroke, and stroke-dasharray to make a dotted loop. Then animate stroke-dashoffset in DevTools and feel Part 6 coming. {<circle> với fill="none", stroke dày, và stroke-dasharray để thành vòng chấm. Rồi animate stroke-dashoffset trong DevTools và cảm nhận Phần 6 sắp tới.}
  3. One gradient, three shapes {Một gradient, ba hình}: define a single gradient in <defs> and fill a rect, a circle, and a path with it. {định nghĩa một gradient trong <defs> và fill một rect, một circle, một path bằng nó.}

What’s next {Phần tiếp theo}

Shapes, paths, and paint — you can now draw and color anything. {Hình, path, và sơn — giờ con vẽ và tô được mọi thứ.} Next we add words. {Tiếp theo ta thêm chữ.} In Part 4 we master SVG text: precise positioning with x/y/dx/dy, styling spans with <tspan>, and the showpiece — flowing a label along any curve with <textPath>. {Ở Phần 4 ta làm chủ text SVG: định vị chính xác bằng x/y/dx/dy, style span bằng <tspan>, và món đinh — uốn nhãn chạy theo mọi đường cong bằng <textPath>.}