SVG from Zero to Senior · Part 21 — Choreographing a Scene with SMIL
Part 21 opens a bonus Motion & Scenes arc: direct a whole scene, not one element. Use SMIL syncbase timing (begin="other.end") to sequence sky, sun, clouds and title into a self-playing, looping scene — no JS clock. With a live demo.
You finished the core twenty parts — you’re a senior. {Bạn đã xong hai mươi phần cốt lõi — bạn là senior.} This part opens a bonus Motion & Scenes arc {Phần này mở một nhánh bonus Motion & Scenes}. Back in Part 6 you animated a single element. {Ở Phần 6 bạn animate một phần tử đơn.} A scene is different: many elements moving in a deliberate sequence, like a director calling cues. {Một hoạt cảnh thì khác: nhiều phần tử chuyển động theo một trình tự có chủ ý, như đạo diễn hô khẩu lệnh.} The beautiful thing about SMIL is that the SVG can choreograph itself — no JavaScript timeline required. {Điều đẹp của SMIL là SVG có thể tự dàn dựng chính nó — không cần timeline JavaScript.}
Hit Replay and watch the cues fire in order: sky brightens → sun rises → clouds drift → title fades up → loop. {Bấm Replay và xem các khẩu lệnh nổ theo thứ tự: trời sáng → mặt trời mọc → mây trôi → tiêu đề hiện → lặp lại.}
Open the full demo {Mở demo đầy đủ}: /tools/svg-scene-smil-demo/.
The one idea: syncbase timing {Ý tưởng cốt lõi: định thời syncbase}
Most people set begin to a clock time — begin="2s". {Đa số đặt begin theo giờ đồng hồ — begin="2s".} That’s brittle: change one duration and you must hand-recompute every later number. {Cái đó dễ vỡ: đổi một thời lượng là phải tự tính lại mọi con số sau đó.} The senior move is syncbase timing — begin relative to another animation’s lifecycle: {Nước đi senior là định thời syncbase — bắt đầu tương đối với vòng đời của animation khác:}
<animate id="a_sky" attributeName="opacity" from="0.35" to="1"
begin="0s" dur="1.4s" fill="freeze"/>
<!-- starts the instant the sky animation ENDS -->
<animate id="a_sun" attributeName="cy" from="178" to="96"
begin="a_sky.end" dur="1.6s" fill="freeze"/>
begin="a_sky.end" means “start when a_sky finishes.” {begin="a_sky.end" nghĩa là “bắt đầu khi a_sky xong.”} You can offset it (a_sun.end+0.3s), chain off begin instead of end (a_sun.begin+0.5s), or trigger off events (click, mouseover). {Bạn có thể lệch (a_sun.end+0.3s), móc vào begin thay vì end, hay kích bằng sự kiện.} Change the sun’s dur and the clouds and title slide later automatically — the timeline is declarative and self-healing. {Đổi dur của mặt trời thì mây và tiêu đề tự dời theo — timeline khai báo và tự chữa lành.}
fill="freeze" — hold the final frame {fill="freeze" — giữ khung cuối}
By default a SMIL animation snaps back to its original value when it ends — fatal for a scene that builds up. {Mặc định animation SMIL bật về giá trị gốc khi kết thúc — chí mạng cho một cảnh dựng dần.} fill="freeze" holds the end state so each finished cue stays on stage while the next plays. {fill="freeze" giữ trạng thái cuối để mỗi khẩu lệnh đã xong ở lại sân khấu khi cái kế tiếp diễn.}
Easing inside SMIL: keySplines {Easing trong SMIL: keySplines}
SMIL is linear unless you tell it otherwise. {SMIL tuyến tính trừ khi bạn bảo khác.} Set calcMode="spline" and give a cubic-Bézier control curve per segment — the same easing math from the CSS parts: {Đặt calcMode="spline" và cho một đường Bézier bậc ba mỗi đoạn — cùng toán easing từ các phần CSS:}
<animate attributeName="cy" from="178" to="96" dur="1.6s" fill="freeze"
calcMode="spline" keyTimes="0;1" keySplines="0.2 0.8 0.2 1"/>
That keySplines is an ease-out — the sun decelerates as it settles, instead of stopping dead. {keySplines đó là ease-out — mặt trời giảm tốc khi yên vị, thay vì dừng phắt.}
Making the whole scene loop {Cho cả cảnh lặp lại}
The trick: feed the last cue’s end back into the first cue’s begin as a second start time. {Mẹo: đưa kết thúc của khẩu lệnh cuối vào begin của khẩu lệnh đầu như một thời điểm bắt đầu thứ hai.}
<animate id="a_sky" begin="0s; a_title.end+1.2s" .../>
A semicolon-separated begin list gives multiple start times; the second one fires 1.2s after the title finishes, restarting the chain — a self-perpetuating scene. {Một danh sách begin ngăn cách bằng chấm phẩy cho nhiều thời điểm bắt đầu; cái thứ hai nổ 1.2s sau khi tiêu đề xong, khởi động lại chuỗi — một cảnh tự duy trì.}
Driving it from JavaScript when you need to {Điều khiển từ JavaScript khi cần}
SMIL exposes the SVG document timeline. {SMIL phơi bày timeline của tài liệu SVG.} You rarely need JS, but for a Replay button it’s one call: {Bạn hiếm khi cần JS, nhưng cho nút Replay chỉ một lệnh:}
svg.setCurrentTime(0); // rewind the whole timeline → scene replays
// el.beginElement(); // manually fire one animation
// el.endElement(); // cut one short
The master’s warnings {Lời cảnh báo của sư phụ}
fill="freeze"or your scene resets. Every build-up cue needs it, or finished elements vanish mid-scene. {fill="freeze"hoặc cảnh của bạn reset. Mọi khẩu lệnh dựng dần cần nó.}- Chain on
.end, don’t hard-code seconds. Hard-coded times turn one tweak into ten edits. {Móc vào.end, đừng cứng hóa giây.} - SMIL is unsupported in some build/animation contexts. It’s solid in modern browsers for declarative scenes, but for complex product motion reach for the libraries from Part 18. {SMIL không được hỗ trợ ở vài ngữ cảnh. Nó vững trong trình duyệt hiện đại cho cảnh khai báo, nhưng chuyển động sản phẩm phức tạp thì dùng thư viện Phần 18.}
- Respect
prefers-reduced-motion. Offer a static “final frame” for users who opt out — the demo disables its motion under that query. {Tôn trọngprefers-reduced-motion. Cho người chọn tắt một “khung cuối” tĩnh.}
Practice {Thực hành}
- Add a 5th cue {Thêm khẩu lệnh thứ 5}: a bird that flies across after the title, chained with
begin="a_title.end". {một con chim bay ngang sau tiêu đề.} - Re-time the scene {Định lại thời gian cảnh}: double the sun’s
durand confirm every later cue shifts with zero other edits. {nhân đôidurmặt trời và xác nhận mọi khẩu lệnh sau dịch theo, không sửa gì khác.} - Event-driven start {Bắt đầu theo sự kiện}: change the first
begintoclickso the scene plays only when tapped. {đổibeginđầu thànhclick.}
What’s next {Phần tiếp theo}
You can sequence a scene in time. {Bạn dàn được một cảnh theo thời gian.} Next we make things travel through space along arbitrary curves. {Tiếp theo ta cho mọi thứ di chuyển qua không gian dọc các đường cong bất kỳ.} In Part 22 we master motion paths — animateMotion, <mpath>, and orient="auto" so an object follows a road, a comet streaks along an arc, and a label rides a curve while pointing the right way. {Ở Phần 22 ta làm chủ motion path — animateMotion, <mpath>, và orient="auto" để một vật đi theo con đường, một sao chổi lướt theo cung, và một nhãn cưỡi đường cong mà vẫn hướng đúng.}