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