jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

SVG from Zero to Senior · Part 5 — Transforms, Groups & Nested Coordinate Systems

Move and reuse SVG: the transform attribute (translate/scale/rotate/skew), why transform order matters, grouping with <g>, transform-origin, and the power of nested coordinate systems with inner <svg>. With a live order-flipping sandbox.

So far everything has lived in one flat coordinate system, drawn exactly where you typed it. {Tới giờ mọi thứ sống trong một hệ toạ độ phẳng, vẽ đúng chỗ con gõ.} Now we learn to move the grid itself. {Giờ ta học cách di chuyển chính cái lưới.} This is the chapter where SVG stops being static drawings and starts becoming reusable components. {Đây là chương mà SVG thôi là bản vẽ tĩnh và bắt đầu thành component tái dùng.}

Drag the sliders, then hit the order button to flip translate → rotate into rotate → translate and watch the square land somewhere completely different. {Kéo slider, rồi bấm nút đổi thứ tự để lật translate → rotate thành rotate → translate và xem hình vuông đáp xuống chỗ hoàn toàn khác.}

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

The mental shift: you transform the grid, not the shape {Cú lật tư duy: con biến đổi cái lưới, không phải hình}

This is the key insight that makes everything click. {Đây là cái nhìn then chốt làm mọi thứ thông suốt.} A transform doesn’t move an object through space — it re-defines the coordinate system the object is drawn in. {Một transform không di chuyển vật thể trong không gian — nó định nghĩa lại hệ toạ độ mà vật thể được vẽ trong đó.} translate(40, 0) means “shift this element’s personal graph paper 40 units right, then draw it normally.” {translate(40, 0) nghĩa là “dịch giấy kẻ ô riêng của element này 40 đơn vị sang phải, rồi vẽ như bình thường.”} Once you think in moving grids, transform order stops being mysterious. {Khi con nghĩ theo lưới di chuyển, thứ tự transform thôi bí ẩn.}

The transform functions {Các hàm transform}

<g transform="translate(40 20) rotate(30) scale(1.5) skewX(10)"> … </g>
  • translate(tx ty) — shift the origin. {dịch gốc toạ độ.}
  • scale(s) or scale(sx sy) — stretch the grid (also scales stroke width and position!). {kéo lưới (scale luôn cả stroke width và vị trí!).}
  • rotate(deg) or rotate(deg cx cy) — rotate around the origin, or around an explicit point (cx,cy). {xoay quanh gốc, hoặc quanh một điểm (cx,cy).}
  • skewX(deg) / skewY(deg) — slant the grid. {nghiêng lưới.}

You can also write a raw matrix(a b c d e f) — every transform compiles down to one — but you almost never hand-write it. {Con cũng viết được matrix(a b c d e f) thô — mọi transform đều biên dịch về một cái — nhưng gần như không bao giờ viết tay.}

Why order matters (the #1 transform bug) {Vì sao thứ tự quan trọng (lỗi transform số 1)}

Transforms in the attribute apply left to right as nested grids, which means the last one is closest to the shape. {Transform trong thuộc tính áp dụng trái sang phải như các lưới lồng nhau, nghĩa là cái cuối gần hình nhất.} The practical effect: {Hệ quả thực tế:}

<!-- translate first: move right, THEN spin in place -->
<g transform="translate(40 0) rotate(45)"> … </g>

<!-- rotate first: spin around the origin, which FLINGS it on an arc -->
<g transform="rotate(45) translate(40 0)"> … </g>

In the second case, rotating first turns the whole coordinate system, so the later translate(40 0) moves along the rotated X-axis — the shape swings out on an arc. {Ở trường hợp hai, xoay trước làm xoay cả hệ toạ độ, nên translate(40 0) sau đó di chuyển dọc trục X đã xoay — hình văng ra theo cung tròn.} This is the classic “why is my icon flying off?” bug. {Đây là lỗi kinh điển “sao icon của tôi bay đi đâu mất?”.} Flip the order button in the demo until it’s reflex. {Bấm nút đổi thứ tự trong demo cho tới khi thành phản xạ.}

transform-origin — pick your pivot {transform-origin — chọn điểm xoay}

By default rotation and scale pivot around the SVG origin (0,0) — the top-left — which is rarely what you want. {Mặc định xoay và scale lấy gốc SVG (0,0) — góc trên-trái — làm tâm, hiếm khi là cái con muốn.} Two fixes: {Hai cách sửa:}

<!-- 1. the function's built-in centre argument -->
<rect transform="rotate(45 100 100)" />

<!-- 2. CSS transform-origin (works on SVG elements too) -->
<rect style="transform: rotate(45deg); transform-origin: 100px 100px" />

Senior tip: prefer the CSS transform property for animated transforms (it composites on the GPU and respects transform-origin cleanly), and the transform attribute for static structural layout. {Mẹo senior: ưu tiên property transform của CSS cho transform có animation (nó composite trên GPU và tôn trọng transform-origin gọn gàng), và thuộc tính transform cho bố cục tĩnh.}

Groups — the <g> element {Nhóm — element <g>}

<g> bundles elements so one transform (or one fill, opacity, class…) applies to all of them. {<g> gom các element để một transform (hay một fill, opacity, class…) áp cho tất cả.}

<g transform="translate(20 60)" fill="#60a5fa">
  <circle cx="32" cy="32" r="18" />
  <text x="58" y="30">Card</text>
</g>

Move the group → everything moves together. {Di nhóm → mọi thứ di cùng.} This is how you build a chart axis, a labelled icon, or a draggable component: structure it as a group and transform the group as a unit. {Đây là cách con dựng trục biểu đồ, icon có nhãn, hay component kéo được: cấu trúc nó thành nhóm và transform cả nhóm như một khối.} Groups also nest, multiplying their transforms down the tree. {Nhóm cũng lồng nhau, nhân các transform xuống cây.}

Nested <svg> — a fresh coordinate system {<svg> lồng — một hệ toạ độ mới tinh}

The senior move: an <svg> can contain another <svg>. {Nước cờ senior: một <svg> có thể chứa một <svg> khác.} The inner one establishes a brand-new viewport and coordinate system, with its own x, y, width, height, and even its own viewBox. {Cái bên trong thiết lập một viewport và hệ toạ độ hoàn toàn mới, với x, y, width, height riêng, thậm chí viewBox riêng.}

<svg viewBox="0 0 200 200">
  <svg x="100" y="20" width="80" height="80" viewBox="0 0 10 10">
    <!-- here coordinates run 0–10, mapped into that 80×80 box -->
    <circle cx="5" cy="5" r="4" fill="#c8ff00" />
  </svg>
</svg>

This lets you drop in a self-contained widget that doesn’t care about the outer coordinates — perfect for embedding a reusable icon at any size and position without recomputing every number. {Cái này cho con thả vào một widget độc lập không quan tâm toạ độ bên ngoài — hoàn hảo để nhúng một icon tái dùng ở mọi kích thước và vị trí mà không phải tính lại từng con số.} It’s also how <symbol> + <use> sprites work under the hood, which we cover in Part 10. {Đó cũng là cách sprite <symbol> + <use> hoạt động bên dưới, ta nói ở Phần 10.}

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

  • Order is right-to-left for the shape. translate then rotaterotate then translate. {Thứ tự là phải-sang-trái với hình. translate rồi rotaterotate rồi translate.}
  • scale scales strokes too. A 2px stroke under scale(3) renders 6px. Use vector-effect="non-scaling-stroke" to keep it crisp. {scale scale cả stroke. Stroke 2px dưới scale(3) thành 6px. Dùng vector-effect="non-scaling-stroke" để giữ nét.}
  • Default pivot is (0,0). Rotations swing wildly until you set a centre. {Tâm mặc định là (0,0). Phép xoay văng tứ tung cho tới khi con đặt tâm.}

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

  1. Clock hand {Kim đồng hồ}: a thin rect rotated with rotate(deg 100 100) around the clock centre. Change the angle and watch it sweep. {một rect mảnh xoay bằng rotate(deg 100 100) quanh tâm đồng hồ.}
  2. The order experiment {Thí nghiệm thứ tự}: build the flinging-arc bug on purpose with rotate() translate(), then fix it by swapping. {cố tình tạo lỗi văng-cung với rotate() translate(), rồi sửa bằng cách đổi chỗ.}
  3. Grouped badge {Huy hiệu nhóm}: a <g> containing a circle + text; move the whole badge with one translate. {một <g> chứa circle + text; di cả huy hiệu bằng một translate.}

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

You can now move, rotate, scale, group, and nest — the structural toolkit of a senior. {Giờ con di, xoay, scale, gom nhóm, và lồng được — bộ đồ nghề cấu trúc của senior.} Time to make it move over time. {Đến lúc làm nó chuyển động theo thời gian.} In Part 6 we animate: the famous line-drawing effect with stroke-dasharray/stroke-dashoffset, CSS keyframes vs native SMIL <animate>/<animateTransform>, and moving an element along a path with <animateMotion>. {Ở Phần 6 ta animate: hiệu ứng vẽ-đường trứ danh với stroke-dasharray/stroke-dashoffset, CSS keyframes vs SMIL <animate>/<animateTransform> thuần, và di chuyển element dọc path với <animateMotion>.}