SVG from Zero to Senior · Part 13 — Generative Patterns & Backgrounds
Make tiny, resolution-independent textures from a few numbers: repeating <pattern> tiles for hero backgrounds, an organic blob generator with smooth curves, and shipping SVG as a compact data-URI. With a live generator.
Một ảnh nền hero PNG 12 KB, hay một SVG 250 byte sắc lẹm trên mọi màn hình và đổi theme bằng một biến? Cho lưới chấm, gạch chéo, texture tinh tế, và blob hữu cơ, SVG thắng cách biệt. Phần này là phần vui — sinh đồ hoạ từ một nắm con số.
Đổi pattern, chỉnh ô, rồi re-roll máy sinh blob.
Mở demo đầy đủ:
Ôn pattern lặp → nâng cấp
Ở Phần 3 con đã gặp <pattern>. Đây là thực tế production: một ô tí hon lấp đầy vùng bất kỳ kích thước, sắc vô hạn, gần như không tốn byte.
<svg>
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="11" cy="11" r="3" fill="#c8ff00" />
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#dots)" />
</svg>
Đổi nội dung ô lấy texture con muốn:
- một
<circle>. <path d="M22 0H0V22">.<path d="M0 22L22 0">.- cả hai đường chéo trong một ô.
patternUnits="userSpaceOnUse" giữ ô kích thước cố định; patternTransform="rotate(30)" xoay cả texture mà không vẽ lại ô.
Blob hữu cơ — ngẫu nhiên có kiểm soát
Các hình “blob” mềm sau các section hero hiện đại trông như vẽ tay nhưng thuần là toán. Công thức:
- Đặt
nđiểm đều quanh một vòng tròn (góc0 → 2π). - Random bán kính mỗi điểm một chút — đó là độ bất quy tắc.
- Nối chúng bằng Bézier bậc ba mượt qua một path kín để không có góc.
function makeBlob(n, irregularity, base = 70, cx = 100, cy = 100) {
const pts = Array.from({ length: n }, (_, i) => {
const a = (i / n) * Math.PI * 2;
const r = base + (Math.random() * 2 - 1) * irregularity;
return [cx + Math.cos(a) * r, cy + Math.sin(a) * r];
});
// Catmull-Rom → cubic control points for a smooth CLOSED path
let d = `M ${pts[0][0]} ${pts[0][1]}`;
for (let i = 0; i < n; i++) {
const p0 = pts[(i - 1 + n) % n], p1 = pts[i],
p2 = pts[(i + 1) % n], p3 = pts[(i + 2) % n];
const c1 = [p1[0] + (p2[0] - p0[0]) / 6, p1[1] + (p2[1] - p0[1]) / 6];
const c2 = [p2[0] - (p3[0] - p1[0]) / 6, p2[1] - (p3[1] - p1[1]) / 6];
d += ` C ${c1[0]} ${c1[1]}, ${c2[0]} ${c2[1]}, ${p2[0]} ${p2[1]}`;
}
return d + ' Z';
}
Ít điểm + bất quy tắc thấp = viên sỏi êm; nhiều điểm + bất quy tắc cao = vệt bắn. Phép chuyển Catmull-Rom→Bézier (control point /6) là mánh giữ đường cong mượt và kín.
Ship nó dưới dạng data-URI
Một SVG sinh ra có thể sống trọn trong background-image của CSS — không request mạng thêm, không file:
.hero {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \
width='22' height='22'%3E%3Ccircle cx='11' cy='11' r='3' fill='%23c8ff00'/%3E%3C/svg%3E");
}
Luật cho data-URI sạch:
- Dùng dấu nháy đơn trong SVG để khỏi escape nháy kép của CSS.
- URL-encode
#thành%23và</>nếu cần; ưu tiên URL-encode hơn base64. - Giữ nó nhỏ — data-URI không cache riêng, nên đừng inline minh hoạ khổng lồ kiểu này.
Các công cụ như Hero Patterns và SVG Backgrounds chỉ là kỹ thuật này kèm UI.
Lời cảnh báo của sư phụ
- Đóng path blob bằng
Zvà cấp control point vòng quanh, nếu không sẽ có khe hở hoặc gãy ở mối nối. userSpaceOnUsevsobjectBoundingBoxđổi việc kích thước ô là tuyệt đối hay tương đối.- Đừng base64 SVG nhỏ cho CSS — URL-encode nhỏ hơn và đọc được.
Luyện tập, không thì coi như chưa học
- Hero lưới chấm: dựng
<pattern>chấm, fill rect tràn viền, rồi export thành data-URIbackground-image. - Khung avatar blob: sinh một blob và dùng làm
clipPathcho một ảnh. - Texture động: animate
patternTransformđể nền đường chéo trôi chậm.
Phần tiếp theo
Giờ con sinh texture và hình theo lập trình được. Tiếp theo ta đặt SVG vào framework đa số các con dùng. Ở Phần 14 ta nói về SVG trong React/JSX: inline vs import, các bẫy thuộc tính JSX (className, strokeWidth), biến icon thành component có kiểu với props, theme currentColor, và render SVG theo dữ liệu kiểu React.