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.

Tới giờ mọi thứ sống trong một hệ toạ độ phẳng, vẽ đúng chỗ con gõ. Giờ ta học cách di chuyển chính cái lưới. Đâ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.

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.

Mở demo đầy đủ:

Cú lật tư duy: con biến đổi cái lưới, không phải hình

Đây là cái nhìn then chốt làm mọi thứ thông suốt. 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) 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.” Khi con nghĩ theo lưới di chuyển, thứ tự transform thôi bí ẩn.

Các hàm transform

<g transform="translate(40 20) rotate(30) scale(1.5) skewX(10)"> … </g>
  • dịch gốc toạ độ.
  • kéo lưới (scale luôn cả stroke width và vị trí!).
  • xoay quanh gốc, hoặc quanh một điểm (cx,cy).
  • nghiêng lưới.

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.

Vì sao thứ tự quan trọng (lỗi transform số 1)

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

Ở 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. Đây là lỗi kinh điển “sao icon của tôi bay đi đâu mất?”. Bấm nút đổi thứ tự trong demo cho tới khi thành phản xạ.

transform-origin — chọn điểm xoay

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

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.

Nhóm — element <g>

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

Di nhóm → mọi thứ di cùng. Đâ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. Nhóm cũng lồng nhau, nhân các transform xuống cây.

<svg> lồng — một hệ toạ độ mới tinh

Nước cờ senior: một <svg> có thể chứa một <svg> khác. 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>

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ố. Đó cũng là cách sprite <symbol> + <use> hoạt động bên dưới, ta nói ở Phần 10.

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

  • Thứ tự là phải-sang-trái với hình. translate rồi rotaterotate rồi translate.
  • scale scale cả stroke. Stroke 2px dưới scale(3) thành 6px. Dùng vector-effect="non-scaling-stroke" để giữ nét.
  • Tâm mặc định là (0,0). Phép xoay văng tứ tung cho tới khi con đặt tâm.

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

  1. Kim đồng hồ: một rect mảnh xoay bằng rotate(deg 100 100) quanh tâm đồng hồ.
  2. Thí nghiệm thứ tự: 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. Huy hiệu nhóm: một <g> chứa circle + text; di cả huy hiệu bằng một translate.

Phần tiếp theo

Giờ con di, xoay, scale, gom nhóm, và lồng được — bộ đồ nghề cấu trúc của senior. Đến lúc làm nó chuyển động theo thời gian. Ở 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>.