SVG from Zero to Senior · Part 19 — Performance & Rendering Internals
How the browser actually paints SVG, why thousands of DOM nodes get slow, the real cost of filters and huge paths, transform vs cx/cy animation, and the exact signals that tell you to switch to canvas or WebGL. With a live FPS stress test.
Senior không chỉ làm SVG chạy — họ biết khi nào nó sẽ ngừng chạy và vì sao. SVG đẹp và sắc, nhưng có một trần cứng, và vượt qua nó biến UI mượt thành trình chiếu. Phần này về hiểu cỗ máy để con ở dưới cái trần đó — hoặc biết khi nào rời SVG hẳn.
Kéo số node lên và xem FPS của chính máy con, rồi đổi chế độ animation và bật/tắt blur.
Mở demo đầy đủ:
Trình duyệt render SVG thế nào
SVG là đồ hoạ retained-mode: mỗi hình là một node DOM thật mà trình duyệt theo dõi, có style, layout, và paint, như element HTML. Pipeline thô mỗi frame:
- phân giải CSS/thuộc tính cho mỗi node.
- tính hình học (vị trí, ảnh hưởng của
viewBox, transform). - raster hoá hình thành pixel (fill, stroke, gradient, filter).
- ghép các layer lên màn hình.
Tương phản then chốt với <canvas>: canvas là immediate-mode — một element giữ buffer pixel; con vẽ lại, trình duyệt không theo dõi gì. Vì thế canvas scale tới hàng chục nghìn hình trong khi việc ghi sổ từng-node của SVG thì không.
Vì sao hàng nghìn node chậm
Mỗi node SVG mang chi phí ở cả bốn giai đoạn pipeline, cộng overhead bộ nhớ và xử lý sự kiện. Ở vài trăm node động đa số máy giữ 60 FPS; ở vài nghìn, layout + paint mỗi frame phá vỡ ngân sách ~16 ms và FPS sụp đổ. Demo cho con tìm con số của máy con — để ý khi đồng hồ chuyển vàng rồi đỏ.
Bậc thang chi phí animation
Không phải animation nào cũng như nhau. Từ rẻ nhất tới đắt nhất:
- do compositor xử lý, thường bỏ qua layout và paint hoàn toàn. Rẻ nhất.
- kích hoạt layout + paint mỗi frame. Đắt rõ rệt ở quy mô.
- raster lại một vùng mỗi frame; blur lớn là thứ đắt nhất con animate được.
Đổi demo từ transform sang cx/cy ở số node cao và xem FPS rớt — cùng chuyển động, chi phí rất khác. Rồi bật blur.
Tối ưu thực dụng
- Animate
transform/opacity, không phải thuộc tính hình học. will-change: transformtrên element sắp animate, để nâng lên layer riêng — nhưng dùng tiết kiệm; mỗi layer tốn bộ nhớ.- Đơn giản hoá path. Ít điểm = ít phải raster. Chạy SVGO; giảm độ chính xác toạ độ.
- Giảm số node. Gộp hình tĩnh thành một
<path>; làm phẳng nhóm; loại node ngoài màn hình. - Giới hạn vùng filter.
x/y/width/heightchật trên<filter>nghĩa là ít vùng phải raster lại. - Gộp ghi DOM. Dựng chuỗi và set
innerHTMLmột lần, hoặc dùngDocumentFragment. - Tiết chế theo rAF. Đừng cập nhật SVG nhanh hơn tốc độ frame.
Tín hiệu rời SVG
Đổi công nghệ render khi gặp những dấu hiệu này:
- Hơn ~1.000–2.000 node động — chuyển
<canvas>2D. - Hàng chục nghìn điểm / hạt, hoặc hiệu ứng theo từng pixel — chuyển WebGL.
- Luồng dữ liệu thời gian thực vẽ lại liên tục — canvas, hoặc lai: canvas cho lớp dày đặc, SVG cho overlay tương tác sắc nét.
Giữ SVG cho thứ nó vô địch: UI vector sắc, truy cập được, tương tác, scale được ở số node vừa phải.
Lời cảnh báo của sư phụ
- Thuộc tính hình học re-layout mỗi frame. Nếu animation 500 node giật, có lẽ con đang animate
cx/x, không phảitransform. - Filter động là sát thủ FPS. Đừng animate
stdDeviationtrên vùng lớn mỗi frame. - Đo, đừng đoán. Dùng panel Performance của DevTools và đồng hồ FPS; nút cổ chai hiếm khi ở chỗ con tưởng.
Luyện tập, không thì coi như chưa học
- Tìm trần của con: trong demo, tăng node tới khi FPS chạm 30 — ghi con số cho
transformvscx/cy. - Đo một cảnh thật: ghi một trace Performance của SVG động và tìm các spike layout/paint.
- Chuyển sang canvas: lấy một SVG 3.000 hạt bị giật và cài lại trên
<canvas>; so FPS.
Phần tiếp theo
Giờ con biết giới hạn của SVG và cách tôn trọng chúng. Cho màn kết thực thụ, ta ghép mọi thứ lại. Ở Phần 20 ta dựng một biểu đồ từ đầu — không D3, không Chart.js — cài scale, trục, tick, gridline, nhãn, chú giải, và co giãn theo kích thước bằng tay, để con cuối cùng hiểu mọi thư viện biểu đồ đang làm gì cho con.