jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

SVG from Zero to Senior · Part 6 — Animation: Line-Drawing, CSS & SMIL

Make SVG move: the famous self-drawing line with stroke-dasharray/stroke-dashoffset, CSS keyframes vs native SMIL <animate>/<animateTransform>, motion along a path with animateMotion, and when to pick which. With a scrubbable studio.

4 MIN READ

Đây là phần ai cũng âm thầm chờ. SVG có animation là thứ làm logo tự vẽ, icon biến hình, loader quay, biểu đồ mọc lên. Tin tốt: SVG animate được theo ba cách, và senior biết cái nào để chọn.

Kéo dấu tích, bấm play, tạm dừng chấm bay quanh — rồi mới đọc cách từng kỹ thuật hoạt động.

Mở demo đầy đủ:

Ba con đường tới chuyển động

  1. mặc định hiện đại. Animate transform, opacity, stroke-dashoffset, và đa số property trình bày.
  2. animation khai báo bên trong markup SVG. Độc lập, không CSS hay JS.
  3. cho chuyển động theo tương tác hoặc vật lý.

Quy tắc senior: CSS cho UI app, SMIL cho icon độc lập, JS khi trạng thái hoặc input điều khiển chuyển động.

Mánh vẽ-đường (viên ngọc vương miện)

Hiệu ứng này làm ai cũng trầm trồ, và nó thuần là sự thông minh với hai property con đã gặp ở Phần 3.

Ý tưởng: làm pattern nét đứt thành một nét khổng lồ dài đúng bằng cả path, rồi đẩy nó ra khỏi tầm nhìn bằng stroke-dashoffset, rồi animate offset về 0.

.path {
  stroke-dasharray: 100;     /* one dash = full normalized length */
  stroke-dashoffset: 100;    /* shifted away → invisible */
  animation: draw 2s ease forwards;
}
@keyframes draw {
  to { stroke-dashoffset: 0; }  /* shift back → fully drawn */
}

Bí quyết kích hoạt là pathLength="100" trên element: nó chuẩn hoá chiều dài thật của path về 100, nên con dùng phần trăm tròn thay vì đo hình học thật.

<path d="M 30 75 L 80 115 L 170 30" pathLength="100"
      stroke-dasharray="100" stroke-dashoffset="100" />

Không có pathLength, con phải đọc path.getTotalLength() bằng JS và nhét con số đó vào. Kéo slider trong demo và con đang lái stroke-dashoffset bằng tay theo nghĩa đen.

SMIL — animation nướng sẵn trong markup

Element SMIL nằm trong hình mà nó animate.

<rect x="80" y="50" width="40" height="40">
  <!-- tween any attribute -->
  <animate attributeName="fill"
           values="#60a5fa;#c8ff00;#ef4444;#60a5fa"
           dur="4s" repeatCount="indefinite" />
  <!-- tween a transform -->
  <animateTransform attributeName="transform" type="rotate"
                    from="0 100 70" to="360 100 70"
                    dur="4s" repeatCount="indefinite" />
</rect>
  • tween một thuộc tính thường.
  • tween transform với một type.
  • values liệt kê keyframe; dur, repeatCount, begin điều khiển thời gian.

SMIL chuyển giao tuyệt vời — animation đi theo file, nên một icon .svg vẫn animate cả khi là <img src>. Nó từng bị doạ khai tử nhiều năm trước nhưng giờ lại được hỗ trợ trên các trình duyệt hiện đại. Cho UI app ưu tiên CSS; cho icon động chuyển giao được, SMIL lý tưởng.

Chuyển động dọc path

<animateMotion> di element dọc một path — quỹ đạo, băng chuyền, tên lửa theo đường bay.

<path id="track" d="M 20 110 Q 100 0 180 110" fill="none" />
<circle r="7" fill="#c8ff00">
  <animateMotion dur="3s" repeatCount="indefinite" rotate="auto">
    <mpath href="#track" />
  </animateMotion>
</circle>

rotate="auto" xoay element hướng theo chiều di chuyển — cần cho mũi tên, tuỳ chọn cho chấm. Tương đương CSS là offset-path/offset-distance, animate rất mượt trên compositor.

Điều khiển SMIL từ JS

Mánh senior tiện: SVG root có pauseAnimations()unpauseAnimations(), nên con tạm dừng tất cả timeline SMIL một lúc — đúng cái nút pause trong demo làm.

Hiệu năng & khả năng truy cập

  • Animate transformopacity — chạy trên GPU compositor và không gây layout.
  • stroke-dashoffset rẻ và mượt, nên vẽ-đường cảm giác mượt như bơ.
  • Luôn tôn trọng prefers-reduced-motion. Bọc chuyển động lớn trong media query và cho fallback nhẹ nhàng.
@media (prefers-reduced-motion: reduce) {
  .path { animation: none; }
}

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

  • Không pathLength, không phần trăm gọn. Hoặc đặt pathLength="100" hoặc đo bằng getTotalLength().
  • from/to của SMIL cho rotate cần tâm. 100 70 là điểm xoay, giống thuộc tính transform.
  • Đừng animate các thuộc tính gây layout trong vòng lặp nóng. Ưu tiên transform hơn animate x/width cho cảnh lớn.

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

  1. Chữ ký tự vẽ: lấy một path một nét, đặt pathLength="100", và vẽ nó khi tải trang.
  2. Loader SMIL: một circle có ropacity đập mãi — không CSS, không JS.
  3. Quỹ đạo: cho một chấm bay quanh path tròn với <animateMotion>, rồi thêm rotate="auto" cho tam giác và xem nó nghiêng vào khúc cua.

Phần tiếp theo

SVG của con giờ chuyển động, tự vẽ, và bay quanh được. Tiếp theo ta thêm chiều sâu và không khí. Ở Phần 7 ta mở cỗ máy filter của SVG — một pipeline xử lý ảnh tí hon dựng từ các primitive như feGaussianBlur, feColorMatrix, feOffset, và feMerge — để tạo đổ bóng, phát sáng, và hiệu ứng kính mờ thật hoàn toàn trong trình duyệt.