animot-presenter 0.5.25 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -143,6 +143,347 @@ export function textAnimate(node, params) {
143
143
  }
144
144
  raf = requestAnimationFrame(step);
145
145
  }
146
+ // ─── Phase 3 runners (mirrored from animot app) ───────────────────
147
+ function wrapWords(text) {
148
+ const wrap = document.createElement('span');
149
+ wrap.className = 'ta-wrap';
150
+ const words = [];
151
+ const parts = text.split(/(\s+)/);
152
+ for (const p of parts) {
153
+ if (/^\s+$/.test(p))
154
+ wrap.appendChild(document.createTextNode(p));
155
+ else if (p.length > 0) {
156
+ const w = document.createElement('span');
157
+ w.className = 'ta-word';
158
+ w.style.display = 'inline-block';
159
+ w.textContent = p;
160
+ wrap.appendChild(w);
161
+ words.push(w);
162
+ }
163
+ }
164
+ return { wrap, words };
165
+ }
166
+ function runScrambleIn() {
167
+ originalContent = params.content ?? '';
168
+ node.innerHTML = '';
169
+ const wrap = wrapChars(originalContent);
170
+ node.appendChild(wrap);
171
+ const chars = Array.from(wrap.querySelectorAll('.ta-char'));
172
+ const finalText = Array.from(originalContent);
173
+ const stagger = params.stagger ?? Math.max(30, Math.min(80, 800 / Math.max(1, chars.length)));
174
+ const settleMs = 500;
175
+ const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&*';
176
+ const start = performance.now();
177
+ const dur = effectiveDuration();
178
+ function step(now) {
179
+ const elapsed = (now - start) % (params.loop ? Math.max(stagger * chars.length + settleMs, dur) : Number.POSITIVE_INFINITY);
180
+ let allDone = true;
181
+ for (let i = 0; i < chars.length; i++) {
182
+ const local = elapsed - i * stagger;
183
+ if (local < 0) {
184
+ chars[i].textContent = ' ';
185
+ allDone = false;
186
+ continue;
187
+ }
188
+ if (local >= settleMs) {
189
+ chars[i].textContent = finalText[i];
190
+ continue;
191
+ }
192
+ allDone = false;
193
+ chars[i].textContent = finalText[i] === ' ' ? ' ' : alphabet[(Math.random() * alphabet.length) | 0];
194
+ }
195
+ if (params.loop || !allDone)
196
+ raf = requestAnimationFrame(step);
197
+ else
198
+ raf = 0;
199
+ }
200
+ raf = requestAnimationFrame(step);
201
+ }
202
+ function runSlotMachine() {
203
+ originalContent = params.content ?? '';
204
+ node.innerHTML = '';
205
+ const wrap = wrapChars(originalContent);
206
+ node.appendChild(wrap);
207
+ const chars = Array.from(wrap.querySelectorAll('.ta-char'));
208
+ const finalText = Array.from(originalContent);
209
+ const stagger = params.stagger ?? 60;
210
+ const spinMs = 600;
211
+ const reel = '0123456789!@#$%ABCDEFGH';
212
+ const start = performance.now();
213
+ function step(now) {
214
+ const elapsed = now - start;
215
+ let allDone = true;
216
+ for (let i = 0; i < chars.length; i++) {
217
+ const local = elapsed - i * stagger;
218
+ if (local < 0) {
219
+ chars[i].textContent = ' ';
220
+ allDone = false;
221
+ continue;
222
+ }
223
+ if (local >= spinMs) {
224
+ chars[i].textContent = finalText[i];
225
+ chars[i].style.transform = 'translateY(0)';
226
+ continue;
227
+ }
228
+ allDone = false;
229
+ const t = local / spinMs;
230
+ const offsetY = (1 - t) * -0.3;
231
+ chars[i].textContent = finalText[i] === ' ' ? ' ' : reel[((local / 60) | 0) % reel.length];
232
+ chars[i].style.transform = `translateY(${offsetY}em)`;
233
+ }
234
+ if (params.loop || !allDone)
235
+ raf = requestAnimationFrame(step);
236
+ else
237
+ raf = 0;
238
+ }
239
+ for (const c of chars)
240
+ c.style.transform = 'translateY(-0.3em)';
241
+ raf = requestAnimationFrame(step);
242
+ }
243
+ function runDrop() {
244
+ originalContent = params.content ?? '';
245
+ node.innerHTML = '';
246
+ const wrap = wrapChars(originalContent);
247
+ node.appendChild(wrap);
248
+ const chars = Array.from(wrap.querySelectorAll('.ta-char'));
249
+ const stagger = params.stagger ?? Math.max(30, Math.min(90, 700 / Math.max(1, chars.length)));
250
+ const perLetter = 520;
251
+ for (const c of chars) {
252
+ c.style.opacity = '0';
253
+ c.style.transform = 'translateY(-1.2em)';
254
+ }
255
+ const start = performance.now();
256
+ function step(now) {
257
+ const elapsed = now - start;
258
+ let allDone = true;
259
+ for (let i = 0; i < chars.length; i++) {
260
+ const t = Math.min(1, Math.max(0, (elapsed - i * stagger) / perLetter));
261
+ if (t < 1)
262
+ allDone = false;
263
+ let b;
264
+ if (t < 0.5)
265
+ b = 4 * t * t * t;
266
+ else
267
+ b = 1 - Math.pow(-2 * t + 2, 3) / 2;
268
+ const overshoot = t > 0.7 ? Math.sin((t - 0.7) * Math.PI / 0.3) * 0.08 : 0;
269
+ const translate = (1 - b) * -1.2 + overshoot;
270
+ chars[i].style.opacity = String(Math.min(1, t * 2));
271
+ chars[i].style.transform = `translateY(${translate}em)`;
272
+ }
273
+ if (!allDone)
274
+ raf = requestAnimationFrame(step);
275
+ else
276
+ raf = 0;
277
+ }
278
+ raf = requestAnimationFrame(step);
279
+ }
280
+ function runGlitch() {
281
+ originalContent = params.content ?? '';
282
+ node.innerHTML = '';
283
+ const wrap = wrapChars(originalContent);
284
+ node.appendChild(wrap);
285
+ const chars = Array.from(wrap.querySelectorAll('.ta-char'));
286
+ const dur = 1100;
287
+ for (const c of chars) {
288
+ c.style.opacity = '0';
289
+ }
290
+ const start = performance.now();
291
+ function step(now) {
292
+ const elapsed = now - start;
293
+ let allDone = true;
294
+ for (let i = 0; i < chars.length; i++) {
295
+ const localStart = (i / Math.max(1, chars.length)) * (dur * 0.4);
296
+ const t = Math.min(1, Math.max(0, (elapsed - localStart) / (dur - localStart)));
297
+ if (t < 1)
298
+ allDone = false;
299
+ const jitter = t < 0.85 ? (Math.random() - 0.5) * 0.08 * (1 - t) : 0;
300
+ const filter = t < 0.7
301
+ ? `drop-shadow(${(Math.random() - 0.5) * 4}px 0 0 #06b6d4) drop-shadow(${(Math.random() - 0.5) * 4}px 0 0 #ec4899)`
302
+ : 'none';
303
+ chars[i].style.opacity = String(t);
304
+ chars[i].style.transform = `translate(${jitter}em, ${jitter}em)`;
305
+ chars[i].style.filter = filter;
306
+ }
307
+ if (!allDone)
308
+ raf = requestAnimationFrame(step);
309
+ else {
310
+ for (const c of chars) {
311
+ c.style.filter = 'none';
312
+ c.style.transform = 'none';
313
+ }
314
+ raf = 0;
315
+ }
316
+ }
317
+ raf = requestAnimationFrame(step);
318
+ }
319
+ function runMarquee() {
320
+ originalContent = params.content ?? '';
321
+ node.innerHTML = '';
322
+ const wrap = document.createElement('span');
323
+ wrap.className = 'ta-wrap';
324
+ wrap.style.display = 'inline-block';
325
+ wrap.style.whiteSpace = 'nowrap';
326
+ wrap.textContent = originalContent + ' ' + originalContent;
327
+ node.style.overflow = 'hidden';
328
+ node.style.whiteSpace = 'nowrap';
329
+ node.appendChild(wrap);
330
+ const start = performance.now();
331
+ const cycle = effectiveDuration() || 6000;
332
+ function step(now) {
333
+ const elapsed = (now - start) % cycle;
334
+ const pct = elapsed / cycle;
335
+ wrap.style.transform = `translateX(${-50 * pct}%)`;
336
+ raf = requestAnimationFrame(step);
337
+ }
338
+ raf = requestAnimationFrame(step);
339
+ }
340
+ function runBlurIn() {
341
+ originalContent = params.content ?? '';
342
+ node.innerHTML = '';
343
+ const wrap = wrapChars(originalContent);
344
+ node.appendChild(wrap);
345
+ const chars = Array.from(wrap.querySelectorAll('.ta-char'));
346
+ const stagger = params.stagger ?? Math.max(20, Math.min(60, 600 / Math.max(1, chars.length)));
347
+ const perLetter = 600;
348
+ for (const c of chars) {
349
+ c.style.opacity = '0';
350
+ c.style.filter = 'blur(12px)';
351
+ }
352
+ const start = performance.now();
353
+ function step(now) {
354
+ const elapsed = now - start;
355
+ let allDone = true;
356
+ for (let i = 0; i < chars.length; i++) {
357
+ const t = Math.min(1, Math.max(0, (elapsed - i * stagger) / perLetter));
358
+ if (t < 1)
359
+ allDone = false;
360
+ const eased = 1 - Math.pow(1 - t, 2);
361
+ chars[i].style.opacity = String(eased);
362
+ chars[i].style.filter = `blur(${(1 - eased) * 12}px)`;
363
+ }
364
+ if (!allDone)
365
+ raf = requestAnimationFrame(step);
366
+ else {
367
+ for (const c of chars)
368
+ c.style.filter = 'none';
369
+ raf = 0;
370
+ }
371
+ }
372
+ raf = requestAnimationFrame(step);
373
+ }
374
+ function runStretch() {
375
+ originalContent = params.content ?? '';
376
+ node.innerHTML = '';
377
+ const wrap = wrapChars(originalContent);
378
+ node.appendChild(wrap);
379
+ const chars = Array.from(wrap.querySelectorAll('.ta-char'));
380
+ const stagger = params.stagger ?? 50;
381
+ const perLetter = 450;
382
+ for (const c of chars) {
383
+ c.style.opacity = '0';
384
+ c.style.transform = 'scaleY(0.1)';
385
+ c.style.transformOrigin = '50% 100%';
386
+ }
387
+ const start = performance.now();
388
+ function step(now) {
389
+ const elapsed = now - start;
390
+ let allDone = true;
391
+ for (let i = 0; i < chars.length; i++) {
392
+ const t = Math.min(1, Math.max(0, (elapsed - i * stagger) / perLetter));
393
+ if (t < 1)
394
+ allDone = false;
395
+ const eased = 1 - Math.pow(1 - t, 4);
396
+ const overshoot = t > 0.8 ? Math.sin((t - 0.8) * Math.PI / 0.2) * 0.15 : 0;
397
+ const sy = 0.1 + (1 - 0.1) * eased + overshoot;
398
+ chars[i].style.opacity = String(Math.min(1, t * 1.5));
399
+ chars[i].style.transform = `scaleY(${sy})`;
400
+ }
401
+ if (!allDone)
402
+ raf = requestAnimationFrame(step);
403
+ else
404
+ raf = 0;
405
+ }
406
+ raf = requestAnimationFrame(step);
407
+ }
408
+ function runSlideWords() {
409
+ originalContent = params.content ?? '';
410
+ node.innerHTML = '';
411
+ const { wrap, words } = wrapWords(originalContent);
412
+ node.appendChild(wrap);
413
+ const stagger = params.stagger ?? 100;
414
+ const perWord = 500;
415
+ for (const w of words) {
416
+ w.style.opacity = '0';
417
+ w.style.transform = 'translateY(0.6em)';
418
+ }
419
+ const start = performance.now();
420
+ function step(now) {
421
+ const elapsed = now - start;
422
+ let allDone = true;
423
+ for (let i = 0; i < words.length; i++) {
424
+ const t = Math.min(1, Math.max(0, (elapsed - i * stagger) / perWord));
425
+ if (t < 1)
426
+ allDone = false;
427
+ const eased = 1 - Math.pow(1 - t, 3);
428
+ words[i].style.opacity = String(eased);
429
+ words[i].style.transform = `translateY(${(1 - eased) * 0.6}em)`;
430
+ }
431
+ if (!allDone)
432
+ raf = requestAnimationFrame(step);
433
+ else
434
+ raf = 0;
435
+ }
436
+ raf = requestAnimationFrame(step);
437
+ }
438
+ function runWave() {
439
+ originalContent = params.content ?? '';
440
+ node.innerHTML = '';
441
+ const wrap = wrapChars(originalContent);
442
+ node.appendChild(wrap);
443
+ const chars = Array.from(wrap.querySelectorAll('.ta-char'));
444
+ const cycle = effectiveDuration() || 1800;
445
+ const amp = 0.18;
446
+ const start = performance.now();
447
+ function step(now) {
448
+ const elapsed = (now - start) / cycle;
449
+ for (let i = 0; i < chars.length; i++) {
450
+ const phase = elapsed * Math.PI * 2 + (i / Math.max(1, chars.length)) * Math.PI * 1.4;
451
+ chars[i].style.transform = `translateY(${Math.sin(phase) * amp}em)`;
452
+ }
453
+ raf = requestAnimationFrame(step);
454
+ }
455
+ raf = requestAnimationFrame(step);
456
+ }
457
+ function runTypewriterErase() {
458
+ originalContent = params.content ?? '';
459
+ const chars = Array.from(originalContent);
460
+ const speed = 50;
461
+ const charMs = 1000 / speed;
462
+ const holdMs = 1200;
463
+ const eraseMs = chars.length * charMs * 0.6;
464
+ const cycle = chars.length * charMs + holdMs + eraseMs + 400;
465
+ const start = performance.now();
466
+ function step(now) {
467
+ const t = (now - start) % cycle;
468
+ let shown;
469
+ if (t < chars.length * charMs)
470
+ shown = Math.floor(t / charMs);
471
+ else if (t < chars.length * charMs + holdMs)
472
+ shown = chars.length;
473
+ else if (t < chars.length * charMs + holdMs + eraseMs) {
474
+ const eraseProgress = (t - chars.length * charMs - holdMs) / eraseMs;
475
+ shown = Math.max(0, chars.length - Math.floor(eraseProgress * chars.length));
476
+ }
477
+ else
478
+ shown = 0;
479
+ node.textContent = chars.slice(0, shown).join('') + (Math.floor(now / 500) % 2 === 0 ? '|' : '');
480
+ if (params.loop || t < cycle)
481
+ raf = requestAnimationFrame(step);
482
+ else
483
+ raf = 0;
484
+ }
485
+ raf = requestAnimationFrame(step);
486
+ }
146
487
  function runHandwriting() {
147
488
  // Real per-character handwriting: each glyph is its own <text> element
148
489
  // with its own stroke-dasharray. We measure char positions from a single
@@ -313,6 +654,36 @@ export function textAnimate(node, params) {
313
654
  case 'handwriting':
314
655
  runHandwriting();
315
656
  break;
657
+ case 'scramble-in':
658
+ runScrambleIn();
659
+ break;
660
+ case 'slot-machine':
661
+ runSlotMachine();
662
+ break;
663
+ case 'drop':
664
+ runDrop();
665
+ break;
666
+ case 'glitch':
667
+ runGlitch();
668
+ break;
669
+ case 'marquee':
670
+ runMarquee();
671
+ break;
672
+ case 'blur-in':
673
+ runBlurIn();
674
+ break;
675
+ case 'stretch':
676
+ runStretch();
677
+ break;
678
+ case 'slide-words':
679
+ runSlideWords();
680
+ break;
681
+ case 'wave':
682
+ runWave();
683
+ break;
684
+ case 'typewriter-erase':
685
+ runTypewriterErase();
686
+ break;
316
687
  default: /* other modes handled elsewhere */ break;
317
688
  }
318
689
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "animot-presenter",
3
- "version": "0.5.25",
3
+ "version": "0.6.0",
4
4
  "description": "Embed animated presentations anywhere. Works with vanilla JS, React, Vue, Angular, Svelte, and any frontend framework. Morphing animations, code highlighting, charts, particles, and more.",
5
5
  "type": "module",
6
6
  "svelte": "./dist/index.js",