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.

Giờ con tả được mọi hình (Phần 1) và mọi đường viền (Phần 2). Nhưng hình không sơn thì vô hình — đến lúc mở hộp màu. 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.

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.

Mở demo đầy đủ:

Thuộc tính trình bày vs CSS

Đầu tiên, một sự thật tầm senior giúp tiết kiệm hàng giờ bối rối. 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" />

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. Đó là lý do con có thể đổi màu icon bằng .icon { fill: currentColor } dù file SVG đã nhúng sẵn fill="#000". Ta khai thác mạnh điều này ở Phần 10.

fill và hai fill rule

fill nhận mọi màu CSS, none, currentColor, hoặc tham chiếu url(#id). 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:

  • đếm hướng cuốn; gần như luôn là cái con muốn.
  • 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" />

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.

Họ stroke

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 */
/>

Vài điều người mới bỏ lỡ:

  • Stroke nằm giữa đường — nửa tràn vào trong, nửa ra ngoài.
  • 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 là mánh đằng sau animation “tự vẽ” ta dựng ở Phần 6. Nhớ cặp đôi này — nó có ích về sau.

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

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> chứa những thứ không vẽ trực tiếp, chỉ để tham chiếu.

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 là hướng của gradient. Mặc định toạ độ là objectBoundingBox (0→1 trên hình); chuyển gradientUnits="userSpaceOnUse" để dùng toạ độ user thật.

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 đặ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 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)" />

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. 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.

Lời cảnh báo của sư phụ

  • 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 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 đè.
  • 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.

Luyện tập, không thì coi như chưa học

  1. Huy hiệu thương hiệu: rect bo góc tô gradient tuyến tính 2 stop và stroke 2px.
  2. Vòng nét đứt: <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. Một gradient, ba hình: định nghĩa một gradient trong <defs> và fill một rect, một circle, một path bằng nó.

Phần tiếp theo

Hình, path, và sơn — giờ con vẽ và tô được mọi thứ. Tiếp theo ta thêm chữ. Ở 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>.