返回 oh-my-ppt
data-anim.md
1 # Data Anim Reference
2
3 Deep-dive into how data-anim works, timing internals, trigger mechanics, scripted animation patterns, and composition examples.
4
5 ## How data-anim maps to anime.js
6
7 Each `data-anim` type generates specific anime.js parameters:
8
9 | data-anim | Effect | anime.js params |
10 |---|---|---|
11 | `fade` | Simple opacity transition | `opacity: [0, 1]` |
12 | `fade-up` | Fade + slide up 20px | `opacity: [0, 1]`, `translateY: [20, 0]` |
13 | `fade-down` | Fade + slide down 20px | `opacity: [0, 1]`, `translateY: [-20, 0]` |
14 | `fade-left` | Fade + slide from right 20px | `opacity: [0, 1]`, `translateX: [20, 0]` |
15 | `fade-right` | Fade + slide from left 20px | `opacity: [0, 1]`, `translateX: [-20, 0]` |
16 | `scale-in` | Fade + scale from 85% | `opacity: [0, 1]`, `scale: [0.85, 1]` |
17 | `slide-up` | Larger slide up 40px | `opacity: [0, 1]`, `translateY: [40, 0]` |
18 | `slide-down` | Larger slide down 40px | `opacity: [0, 1]`, `translateY: [-40, 0]` |
19 | `slide-left` | Larger slide from right 40px | `opacity: [0, 1]`, `translateX: [40, 0]` |
20 | `slide-right` | Larger slide from left 40px | `opacity: [0, 1]`, `translateX: [-40, 0]` |
21 | `fly-in` | Directional entrance, 40px | `opacity: [0, 1]` + translateX/Y based on `from` |
22 | `wipe` | Clip-path reveal | `opacity: [0, 1]`, `clipPath: [hidden, 'inset(0%)']` |
23 | `zoom-in` | Dramatic scale from 75% | `opacity: [0, 1]`, `scale: [0.75, 1]` |
24 | `spin-in` | Rotate + scale | `opacity: [0, 1]`, `rotate: [-12, 0]`, `scale: [0.92, 1]` |
25 | `grow-shrink-soft` | Gentle emphasis pulse | `scale: [0.95, 1.04, 1]` |
26 | `grow-shrink` | Emphasis pulse (no fade) | `scale: [0.9, 1.08, 1]` |
27 | `grow-shrink-strong` | Strong emphasis pulse | `scale: [0.85, 1.12, 1]` |
28 | `pulse-soft` | Very subtle attention | `scale: [1, 1.03, 1]` |
29 | `pulse` | Subtle emphasis (no fade) | `scale: [1, 1.06, 1]` |
30 | `pulse-strong` | Strong attention pulse | `scale: [1, 1.1, 1]` |
31 | `exit-fade` | Fade out | `opacity: [1, 0]` |
32 | `exit-scale` | Soft scale-down exit | `opacity: [1, 0]`, `scale: [1, 0.85]` |
33 | `exit-zoom` | Strong scale-down exit | `opacity: [1, 0]`, `scale: [1, 0.75]` |
34 | `exit-wipe` | Directional wipe out | `opacity: [1, 0]`, `clipPath: [visible, hidden]` |
35 | `exit-fly` | Fly out in direction | `opacity: [1, 0]` + translate out based on `from` |
36 | `path` | Motion along constrained linear path | translateX/Y derived from an inline start/end delta |
37
38 ### Path boundary
39
40 For the editable/exportable lane, `data-anim="path"` is intentionally constrained:
41
42 - use an inline linear path string such as `M 0 0 L 120 30`
43 - do not use a DOM selector such as `#curve`
44 - do not use rich SVG draw/morph/path choreography here
45
46 Reason:
47
48 - the current export/import chain can preserve a linear delta
49 - it cannot preserve arbitrary SVG path semantics as stable editable PPTX motion
50
51 ## Attribute defaults and ranges
52
53 | Attribute | Default | Range / Notes |
54 |---|---|---|
55 | `data-anim-trigger` | `load` | `load`, `with`, `after`, `click` |
56 | `data-anim-sequence` | unset | `with`, `after`. Preferred load-order control for new content. |
57 | `data-anim-click-group` | unset | Stable token. Only for contiguous `click` animations sharing one reveal step. |
58 | `data-anim-duration` | 500ms | Clamped to 100–5000ms. Prefer 300–1200ms |
59 | `data-anim-delay` | 0 | Milliseconds, or `stagger(N)` |
60 | `data-anim-stagger` | unset | Millisecond gap. Preferred over `stagger(N)` for new content. |
61 | `data-anim-from` | Type-dependent | `left`, `right`, `top`, `bottom`, `center` |
62 | `data-anim-easing` | runtime-only | Compatibility only. Do not use in standard editable/exportable pages. |
63 | `data-anim-repeat` | runtime-only | Compatibility only. Do not use in standard editable/exportable pages. |
64 | `data-anim-direction` | runtime-only | Compatibility only. Do not use in standard editable/exportable pages. |
65
66 ## How stagger() works
67
68 `stagger(N)` uses per-trigger-group counters. Within the same trigger group (all `load` elements share one counter, all `click` elements share another):
69
70 - 1st element with `stagger(100)` → delay = 0
71 - 2nd element with `stagger(100)` → delay = 100
72 - 3rd element with `stagger(100)` → delay = 200
73 - 4th element with `stagger(100)` → delay = 300
74
75 This creates a cascade without needing to manually specify each delay.
76
77 ```html
78 <div data-anim="fade-up" data-anim-delay="stagger(120)">Card A</div>
79 <!-- delay: 0 -->
80 <div data-anim="fade-up" data-anim-delay="stagger(120)">Card B</div>
81 <!-- delay: 120 -->
82 <div data-anim="fade-up" data-anim-delay="stagger(120)">Card C</div>
83 <!-- delay: 240 -->
84 ```
85
86 Good stagger values:
87 - 60–80ms: tight, energetic cascade (cards, metrics)
88 - 90–120ms: comfortable, readable sequence (list items, steps)
89 - 150–200ms: dramatic, deliberate reveal (key points, sections)
90
91 Preferred new syntax:
92
93 ```html
94 <div data-anim="fade-up" data-anim-stagger="120">Card A</div>
95 <div data-anim="fade-up" data-anim-stagger="120">Card B</div>
96 <div data-anim="fade-up" data-anim-stagger="120">Card C</div>
97 ```
98
99 ## Trigger mechanics in detail
100
101 ### load (default)
102
103 Animation plays immediately when the page renders. The runtime scans all `[data-anim]` elements and plays load-triggered animations right away.
104
105 ```html
106 <h2 data-anim="fade-up">Title</h2>
107 <p data-anim="fade-up" data-anim-delay="200">Subtitle appears 200ms later</p>
108 ```
109
110 ### with
111
112 Starts at the same time as the previous animated element. Use for grouping: a title and its subtitle should appear together, not sequentially.
113
114 ```html
115 <div class="grid grid-cols-2 gap-4">
116 <div data-anim="fade-up" data-anim-delay="stagger(100)">
117 <h3>Point A</h3>
118 <p>Detail for A</p>
119 </div>
120 <div data-anim="fade-up" data-anim-delay="stagger(100)">
121 <h3>Point B</h3>
122 <p>Detail for B</p>
123 </div>
124 </div>
125 ```
126
127 ### after
128
129 Starts after the previous animation finishes (previous delay + duration). Use for short sequences that tell a story.
130
131 ```html
132 <div data-anim="fade-up">Step 1: Identify</div>
133 <div data-anim="fade-up" data-anim-trigger="after">Step 2: Analyze</div>
134 <div data-anim="fade-up" data-anim-trigger="after">Step 3: Act</div>
135 ```
136
137 The runtime tracks `lastSequenceEnd` internally. Each `after` element's effective delay = previous element's delay + duration.
138
139 For new content, prefer `data-anim-sequence="with|after"` and keep `data-anim-trigger` focused on actual trigger semantics:
140
141 ```html
142 <div data-anim="fade-up">Step 1: Identify</div>
143 <div data-anim="fade" data-anim-sequence="with" data-anim-delay="80">Supporting note</div>
144 <div data-anim="fade-up" data-anim-sequence="after">Step 2: Analyze</div>
145 ```
146
147 ### click
148
149 Waits for the user to click/press. The runtime maintains a click state machine — each click advances to the next animation.
150
151 ```html
152 <div data-anim="zoom-in" data-anim-trigger="click">Reveal on first click</div>
153 <div data-anim="zoom-in" data-anim-trigger="click">Reveal on second click</div>
154 ```
155
156 Click is for explicit presentation control. Do not use click for timelines, processes, or steps — those work better with `stagger` or `after`.
157
158 ### click-group
159
160 Use `data-anim-click-group` when several adjacent click-triggered elements must reveal on the same click step:
161
162 ```html
163 <div data-anim="fade-up" data-anim-trigger="click" data-anim-click-group="reveal">Headline</div>
164 <div data-anim="pulse-soft" data-anim-trigger="click" data-anim-click-group="reveal">Badge</div>
165 <div data-anim="fade" data-anim-trigger="click">Next click step</div>
166 ```
167
168 Rules:
169
170 - only use it with `data-anim-trigger="click"`
171 - keep the grouped elements contiguous in DOM order
172 - use a stable token such as `reveal`, `step-1`, or `milestone-a`
173 - do not use it as a general timeline language
174
175 Export behavior:
176
177 - contiguous grouped click animations compile into one PPTX build step
178 - the first element becomes the step leader and the following grouped elements become with-effects
179 - if the DOM order breaks the group, it becomes a different click step
180
181 ## Preview-only boundary
182
183 - Keep the editable public lane centered on whole-element motion.
184 - `splitText`, per-letter/per-word reveal, SVG morph/draw helpers, and arbitrary motion-path choreography should not be normalized into standard editable page content.
185 - If these richer anime capabilities are explored later, they should live behind a dedicated preview-only lane with explicit expectations that editable PPTX guarantees do not apply.
186 - `data-anim-easing`, `data-anim-repeat`, and `data-anim-direction` currently live in the same category: runtime-only compatibility, not stable editable/exportable semantics.
187
188 ## Initial hidden states
189
190 The runtime handles hidden states automatically. Here's how:
191
192 - **load/with/after triggers**: no hidden state applied. The element animates from the `[from, to]` values directly.
193 - **click-triggered entrance animations** (fade, fade-up, slide-up, zoom-in, etc.): the runtime sets `opacity: 0` and an appropriate `transform` inline, then marks the element with `data-ppt-anim-initialized="1"`.
194 - **click-triggered emphasis/exit animations** (`pulse-soft`, `pulse`, `pulse-strong`, `grow-shrink-soft`, `grow-shrink`, `grow-shrink-strong`, `exit-fade`, `exit-scale`, `exit-zoom`, `exit-wipe`, `exit-fly`): no hidden state — the element is already visible.
195
196 Do not manually set `opacity: 0`, `visibility: hidden`, `display: none`, or inline `opacity:0` on animated elements. The runtime handles this, and manual hidden states conflict with the animation system.
197
198 ## Type selection guide
199
200 ### Entrance animations (elements appearing)
201
202 | Goal | Type | Notes |
203 |---|---|---|
204 | Subtle fade-in | `fade` | For text blocks, annotations |
205 | Standard card entrance | `fade-up` | Default choice for most elements |
206 | Directional emphasis | `fly-in` + `from` | Metrics flying in from the side |
207 | Strong directional entrance | `slide-down` / `slide-right` | When fade-up/left is too subtle but wipe is too hard-edged |
208 | Dramatic hero reveal | `zoom-in` | Key numbers, hero images |
209 | Slide-in bar | `wipe` + `from` | Progress bars, timeline segments |
210 | Playful entrance | `spin-in` | Use sparingly for emphasis |
211
212 ### Emphasis animations (already visible elements)
213
214 | Goal | Type | Notes |
215 |---|---|---|
216 | Very subtle attention | `pulse-soft` | Tight KPI polish, low-distraction |
217 | Subtle attention | `pulse` | Default emphasis for key metrics |
218 | Strong attention | `pulse-strong` | Escalations, urgent callouts |
219 | Gentle grow and settle | `grow-shrink-soft` | Confirmation and secondary emphasis |
220 | Grow and settle | `grow-shrink` | Important callouts |
221 | Strong grow and settle | `grow-shrink-strong` | High-priority moments, use sparingly |
222
223 These bounded emphasis labels are preferred over custom scale arrays because the PPTX export path preserves them through distinct native scale ranges.
224
225 ### Exit animations (elements leaving)
226
227 | Goal | Type | Notes |
228 |---|---|---|
229 | Simple fade-out | `exit-fade` | Replacing content |
230 | Soft scale-down exit | `exit-scale` | Quietly retiring chips, secondary panels, low-drama removals |
231 | Strong scale-down exit | `exit-zoom` | Hero outro, spotlight handoff, more theatrical exits |
232 | Directional wipe-out | `exit-wipe` + `from` | Remove banners, process bars, transient callouts |
233 | Fly off screen | `exit-fly` + `from` | Dramatic exits |
234
235 These exit scale labels are preferred over ad hoc scale arrays because the PPTX export path preserves them through distinct native exit scale ranges and the importer can map those ranges back to the same semantic labels.
236
237 ## Composition patterns
238
239 ### Staggered card grid
240
241 ```html
242 <div class="grid grid-cols-3 gap-4">
243 <div data-anim="fade-up" data-anim-delay="stagger(100)">
244 <p class="text-3xl font-bold">$12M</p>
245 <p class="text-lg text-gray-500">Revenue</p>
246 </div>
247 <div data-anim="fade-up" data-anim-delay="stagger(100)">
248 <p class="text-3xl font-bold">86%</p>
249 <p class="text-lg text-gray-500">Retention</p>
250 </div>
251 <div data-anim="fade-up" data-anim-delay="stagger(100)">
252 <p class="text-3xl font-bold">2.4x</p>
253 <p class="text-lg text-gray-500">ROI</p>
254 </div>
255 </div>
256 ```
257
258 ### Title + content sequence
259
260 ```html
261 <h2 data-anim="fade-up" data-anim-duration="600">Key Insight</h2>
262 <p data-anim="fade" data-anim-trigger="with" data-anim-delay="100" data-anim-duration="500">
263 Supporting explanation appears alongside the title.
264 </p>
265 <div data-anim="fade-up" data-anim-trigger="after" data-anim-duration="500">
266 Evidence card appears after title finishes.
267 </div>
268 ```
269
270 ### Directional fly-in from different sides
271
272 ```html
273 <div class="grid grid-cols-2 gap-6">
274 <div data-anim="fly-in" data-anim-from="left">
275 <h3>Challenge</h3>
276 <p>Traditional approaches fall short.</p>
277 </div>
278 <div data-anim="fly-in" data-anim-from="right">
279 <h3>Solution</h3>
280 <p>Our approach addresses this directly.</p>
281 </div>
282 </div>
283 ```
284
285 ### Hero number with zoom + supporting cards
286
287 ```html
288 <div class="flex flex-col gap-6">
289 <div data-anim="zoom-in" data-anim-duration="800">
290 <p class="text-5xl font-bold">42%</p>
291 <p class="text-lg text-gray-500">Market Growth</p>
292 </div>
293 <div class="grid grid-cols-3 gap-4">
294 <div data-anim="fade-up" data-anim-delay="stagger(80)">Card 1</div>
295 <div data-anim="fade-up" data-anim-delay="stagger(80)">Card 2</div>
296 <div data-anim="fade-up" data-anim-delay="stagger(80)">Card 3</div>
297 </div>
298 </div>
299 ```
300
301 ### Emphasis pulse on a key risk
302
303 ```html
304 <div data-anim="pulse" data-anim-duration="600">
305 <p class="text-xl font-bold text-red-600">Critical Risk</p>
306 <p class="text-lg">Action required before Q3.</p>
307 </div>
308 ```
309
310 ### Click-group with bounded emphasis
311
312 ```html
313 <div class="flex items-center gap-3">
314 <div data-anim="fade-up" data-anim-trigger="click" data-anim-click-group="reveal">
315 Launch risk
316 </div>
317 <div data-anim="pulse-strong" data-anim-trigger="click" data-anim-click-group="reveal">
318 Immediate action
319 </div>
320 </div>
321 <div data-anim="grow-shrink-soft" data-anim-trigger="click">
322 Follow-up mitigation
323 </div>
324 ```
325
326 ## Scripted animation escape hatch
327
328 Use `PPT.animate(targets, params)` only when `data-anim` cannot express the motion — complex timelines, synchronized choreography, or custom easing curves.
329
330 ```js
331 // Staggered card entrance with custom curve
332 PPT.animate(".metric-card", {
333 opacity: [0, 1],
334 translateY: [30, 0],
335 duration: 500,
336 delay: PPT.stagger(100),
337 easing: 'easeOutCubic'
338 })
339 ```
340
341 ### PPT.animate vs data-anim
342
343 | | data-anim | PPT.animate |
344 |---|---|---|
345 | Export to PPTX | Yes, deterministic | Partial |
346 | Syntax | HTML attributes | JavaScript |
347 | Best for | Standard entrance/emphasis/exit | Complex timelines, synchronized groups |
348 | Initial state | Managed automatically | Managed automatically |
349
350 ### Timeline for multi-step choreography
351
352 ```js
353 var tl = PPT.createTimeline(".step-card", {
354 opacity: [0, 1],
355 duration: 400
356 })
357 tl.add({ targets: ".step-1", translateY: [20, 0] }, 0)
358 tl.add({ targets: ".step-2", translateY: [20, 0] }, 200)
359 tl.add({ targets: ".step-3", translateY: [20, 0] }, 400)
360 ```
361
362 ### Scripted stagger
363
364 ```js
365 PPT.animate(".card", {
366 opacity: [0, 1],
367 scale: [0.9, 1],
368 delay: PPT.stagger(80, { start: 200 })
369 })
370 ```
371
372 `PPT.stagger(ms)` is a passthrough to `anime.stagger()` when available, with a built-in fallback.
373
374 ## Easing selection guide
375
376 | Easing | Feel | Best for |
377 |---|---|---|
378 | `easeOutCubic` (default) | Smooth deceleration | Most entrance animations |
379 | `easeOutQuad` | Gentle deceleration | Subtle fades, text |
380 | `easeInOutQuad` | Smooth start and end | Movement across distance |
381 | `easeOutExpo` | Snappy stop | Dramatic entrances, hero numbers |
382 | `spring` | Natural bounce | Playful, emphasis |
383
384 ## Print and export behavior
385
386 In print mode (`?print=1`), `PPT.animate` does not run anime.js. Instead, it computes the final animated CSS values and applies them as inline styles. This ensures charts and animated elements are fully visible in screenshots and PDF exports.
387
388 Elements with `data-ppt-anim-initialized="1"` have their animation styles cleared when entering edit mode, so they remain visible and editable.
389
389 lines MARKDOWN