jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

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.

A senior doesn’t just make SVG work — they know when it will stop working and why. {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 is gorgeous and sharp, but it has a hard ceiling, and crossing it turns a smooth UI into a slideshow. {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.} This part is about understanding the engine so you can stay under that ceiling — or know when to leave SVG entirely. {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.}

Drag the node count up and watch your own device’s FPS, then flip the animation mode and the blur toggle. {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.}

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

How the browser renders SVG {Trình duyệt render SVG thế nào}

SVG is retained-mode graphics: every shape is a real DOM node the browser tracks, with style, layout, and paint, just like HTML elements. {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.} The rough pipeline per frame: {Pipeline thô mỗi frame:}

  1. Style — resolve CSS/attributes for each node. {phân giải CSS/thuộc tính cho mỗi node.}
  2. Layout — compute geometry (positions, the effect of viewBox, transforms). {tính hình học (vị trí, ảnh hưởng của viewBox, transform).}
  3. Paint — rasterize shapes into pixels (fills, strokes, gradients, filters). {raster hoá hình thành pixel (fill, stroke, gradient, filter).}
  4. Composite — combine layers onto the screen. {ghép các layer lên màn hình.}

The crucial contrast with <canvas>: canvas is immediate-mode — it’s one element holding a pixel buffer; you redraw, the browser tracks nothing. {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ì.} That’s why canvas scales to tens of thousands of shapes while SVG’s per-node bookkeeping doesn’t. {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.}

Why thousands of nodes get slow {Vì sao hàng nghìn node chậm}

Every SVG node carries cost in all four pipeline stages, plus memory and event-handling overhead. {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.} At a few hundred animated nodes most devices hold 60 FPS; at a few thousand, layout + paint per frame blows the ~16 ms budget and FPS collapses. {Ở 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 đổ.} The demo lets you find your device’s number — note when the meter goes amber then red. {Demo cho con tìm con số của máy con — để ý khi đồng hồ chuyển vàng rồi đỏ.}

The animation cost ladder {Bậc thang chi phí animation}

Not all animation is equal. From cheapest to most expensive: {Không phải animation nào cũng như nhau. Từ rẻ nhất tới đắt nhất:}

  1. transform + opacity — handled by the compositor, often skipping layout and paint entirely. Cheapest. {do compositor xử lý, thường bỏ qua layout và paint hoàn toàn. Rẻ nhất.}
  2. cx/cy/x/y/width/geometry attributes — trigger layout + paint every frame. Noticeably more expensive at scale. {kích hoạt layout + paint mỗi frame. Đắt rõ rệt ở quy mô.}
  3. Filters (feGaussianBlur, shadows, glows) — re-rasterize a region every frame; a large blur is the most expensive single thing you can animate. {raster lại một vùng mỗi frame; blur lớn là thứ đắt nhất con animate được.}

Flip the demo from transform to cx/cy at high node counts and watch FPS drop — same motion, very different cost. {Đổ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.} Then toggle the blur. {Rồi bật blur.}

Practical optimizations {Tối ưu thực dụng}

  • Animate transform/opacity, not geometry attributes. (The single biggest win — Part 6 said it, here’s the proof.) {Animate transform/opacity, không phải thuộc tính hình học.}
  • will-change: transform on elements you’ll animate, to promote them to their own layer — but use sparingly; every layer costs memory. {will-change: transform trê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ớ.}
  • Simplify paths. Fewer points = less to rasterize. Run SVGO; reduce coordinate precision. {Đơn giản hoá path. Ít điểm = ít phải raster. Chạy SVGO; giảm độ chính xác toạ độ.}
  • Reduce node count. Merge static shapes into one <path>; flatten groups; cull off-screen nodes. {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.}
  • Constrain filter regions. A tight x/y/width/height on a <filter> means less area to re-rasterize (Part 7). {Giới hạn vùng filter. x/y/width/height chật trên <filter> nghĩa là ít vùng phải raster lại.}
  • Batch DOM writes. Build a string and set innerHTML once, or use a DocumentFragment, instead of N separate appendChilds. {Gộp ghi DOM. Dựng chuỗi và set innerHTML một lần, hoặc dùng DocumentFragment.}
  • Throttle to rAF. Never update SVG faster than the frame rate. {Tiết chế theo rAF. Đừng cập nhật SVG nhanh hơn tốc độ frame.}

The signals to leave SVG {Tín hiệu rời SVG}

Switch rendering technology when you hit these: {Đổi công nghệ render khi gặp những dấu hiệu này:}

  • More than ~1,000–2,000 animated nodes — go <canvas> 2D. {Hơn ~1.000–2.000 node động — chuyển <canvas> 2D.}
  • Tens of thousands of points / particles, or per-pixel effects — go WebGL. {Hàng chục nghìn điểm / hạt, hoặc hiệu ứng theo từng pixel — chuyển WebGL.}
  • Real-time data streams redrawing constantly (big trading charts, maps with many markers) — canvas, or a hybrid: canvas for the dense layer, SVG for crisp interactive overlays. {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.}

Keep SVG for what it’s unbeatable at: crisp, accessible, interactive, scalable vector UI at modest node counts. {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.}

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

  • Geometry attributes re-layout every frame. If a 500-node animation janks, you’re probably animating cx/x, not transform. {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ải transform.}
  • Animated filters are the FPS killer. Don’t animate stdDeviation over a large region every frame. {Filter động là sát thủ FPS. Đừng animate stdDeviation trên vùng lớn mỗi frame.}
  • Profile, don’t guess. Use the DevTools Performance panel and the FPS meter; the bottleneck is rarely where you assume. {Đ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.}

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

  1. Find your ceiling {Tìm trần của con}: in the demo, raise nodes until FPS hits 30 — note the number for transform vs cx/cy. {trong demo, tăng node tới khi FPS chạm 30 — ghi con số cho transform vs cx/cy.}
  2. Profile a real scene {Đo một cảnh thật}: record a DevTools Performance trace of an animated SVG and find the layout/paint spikes. {ghi một trace Performance của SVG động và tìm các spike layout/paint.}
  3. Port to canvas {Chuyển sang canvas}: take a 3,000-particle SVG that janks and reimplement it on <canvas>; compare FPS. {lấy một SVG 3.000 hạt bị giật và cài lại trên <canvas>; so FPS.}

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

You now know SVG’s limits and how to respect them. {Giờ con biết giới hạn của SVG và cách tôn trọng chúng.} For the true finale, we put everything together. {Cho màn kết thực thụ, ta ghép mọi thứ lại.} In Part 20 we build a chart from scratch — no D3, no Chart.js — implementing scales, axes, ticks, gridlines, labels, a legend, and responsive sizing by hand, so you finally understand what every charting library is doing for you. {Ở 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.}