animot-presenter 0.6.4 → 0.6.5
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.
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Svelte action for arrow animations. Two strategies depending on mode:
|
|
3
3
|
*
|
|
4
|
-
* • 'draw' / 'undraw' / 'draw-undraw' —
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* • 'draw' / 'undraw' / 'draw-undraw' — reveals the stroke ALONG THE PATH using
|
|
5
|
+
* stroke-dasharray/offset keyed to the path's total length. This follows the
|
|
6
|
+
* real geometry, so curved, looping and spiral arrows draw the way they're
|
|
7
|
+
* shaped (a linear clip-path wipe could only sweep left↔right / top↔bottom).
|
|
8
|
+
* The arrowhead (a separate sub-path) is revealed in step. Any dashed/dotted
|
|
9
|
+
* pattern is overridden during the reveal and restored when fully drawn.
|
|
7
10
|
*
|
|
8
11
|
* • 'flow' — marching ants. Continuously shifts stroke-dashoffset on the
|
|
9
12
|
* inner .arrow-path, so the dash pattern appears to flow along the path
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Svelte action for arrow animations. Two strategies depending on mode:
|
|
3
3
|
*
|
|
4
|
-
* • 'draw' / 'undraw' / 'draw-undraw' —
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* • 'draw' / 'undraw' / 'draw-undraw' — reveals the stroke ALONG THE PATH using
|
|
5
|
+
* stroke-dasharray/offset keyed to the path's total length. This follows the
|
|
6
|
+
* real geometry, so curved, looping and spiral arrows draw the way they're
|
|
7
|
+
* shaped (a linear clip-path wipe could only sweep left↔right / top↔bottom).
|
|
8
|
+
* The arrowhead (a separate sub-path) is revealed in step. Any dashed/dotted
|
|
9
|
+
* pattern is overridden during the reveal and restored when fully drawn.
|
|
7
10
|
*
|
|
8
11
|
* • 'flow' — marching ants. Continuously shifts stroke-dashoffset on the
|
|
9
12
|
* inner .arrow-path, so the dash pattern appears to flow along the path
|
|
@@ -13,55 +16,37 @@
|
|
|
13
16
|
export function arrowClipDraw(node, params) {
|
|
14
17
|
let raf = 0;
|
|
15
18
|
let pathEl = null;
|
|
19
|
+
let headEl = null;
|
|
16
20
|
let baseDash = '';
|
|
17
21
|
let baseOffset = '';
|
|
18
|
-
function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
function restorePath() {
|
|
23
|
+
// Back to the element's natural (fully-drawn) appearance.
|
|
24
|
+
if (pathEl) {
|
|
25
|
+
pathEl.style.strokeDasharray = baseDash;
|
|
26
|
+
pathEl.style.strokeDashoffset = baseOffset;
|
|
27
|
+
}
|
|
28
|
+
if (headEl)
|
|
29
|
+
headEl.style.opacity = '';
|
|
23
30
|
}
|
|
24
31
|
function reset() {
|
|
25
32
|
if (raf)
|
|
26
33
|
cancelAnimationFrame(raf);
|
|
27
34
|
raf = 0;
|
|
28
35
|
node.style.clipPath = '';
|
|
29
|
-
|
|
36
|
+
restorePath();
|
|
30
37
|
}
|
|
31
38
|
function run() {
|
|
32
39
|
reset();
|
|
33
40
|
if (!params.enabled || params.mode === 'none')
|
|
34
41
|
return;
|
|
35
|
-
// Capture the inner arrow path (used by 'flow' mode).
|
|
36
42
|
pathEl = node.querySelector('.arrow-path');
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// arrows reveal left↔right. clip-path inset(top right bottom left).
|
|
43
|
-
const dx = params.endX - params.startX;
|
|
44
|
-
const dy = params.endY - params.startY;
|
|
45
|
-
const horizontal = Math.abs(dx) >= Math.abs(dy);
|
|
46
|
-
const positive = horizontal ? dx >= 0 : dy >= 0;
|
|
47
|
-
const goesPositive = params.reverse ? !positive : positive;
|
|
48
|
-
// Build the clip-path string for a given progress (0 = fully hidden,
|
|
49
|
-
// 100 = fully visible) on the dominant axis.
|
|
50
|
-
function clip(insetPct) {
|
|
51
|
-
if (horizontal) {
|
|
52
|
-
return goesPositive
|
|
53
|
-
? `inset(0 ${insetPct}% 0 0)` // hide from right edge
|
|
54
|
-
: `inset(0 0 0 ${insetPct}%)`; // hide from left edge
|
|
55
|
-
}
|
|
56
|
-
return goesPositive
|
|
57
|
-
? `inset(0 0 ${insetPct}% 0)` // hide from bottom
|
|
58
|
-
: `inset(${insetPct}% 0 0 0)`; // hide from top
|
|
59
|
-
}
|
|
60
|
-
const clipFull = clip(100); // fully hidden (100% inset on hide side)
|
|
61
|
-
const clipNone = clip(0);
|
|
43
|
+
headEl = node.querySelector('.arrow-head');
|
|
44
|
+
if (!pathEl)
|
|
45
|
+
return;
|
|
46
|
+
baseDash = pathEl.style.strokeDasharray || '';
|
|
47
|
+
baseOffset = pathEl.style.strokeDashoffset || '';
|
|
62
48
|
// Round duration so an integer number of cycles fits in slide_duration.
|
|
63
|
-
// Without this, GIF loop boundary
|
|
64
|
-
// invisible → re-draw, which the user perceives as a reset.
|
|
49
|
+
// Without this, a GIF loop boundary can show the arrow mid-draw → snap.
|
|
65
50
|
const requested = Math.max(50, params.duration);
|
|
66
51
|
const dur = params.slideDuration && params.slideDuration > 0 && (params.loop || params.mode === 'flow')
|
|
67
52
|
? params.slideDuration / Math.max(1, Math.round(params.slideDuration / requested))
|
|
@@ -70,21 +55,15 @@ export function arrowClipDraw(node, params) {
|
|
|
70
55
|
const m = params.mode;
|
|
71
56
|
// FLOW: marching ants — animate dashoffset continuously, keep base pattern.
|
|
72
57
|
if (m === 'flow') {
|
|
73
|
-
if (!pathEl)
|
|
74
|
-
return;
|
|
75
|
-
// If the arrow has no inline dasharray (i.e. it's solid), give it one
|
|
76
|
-
// so dashes are visible to flow. Otherwise keep the user's pattern.
|
|
77
58
|
const dashAttr = pathEl.getAttribute('stroke-dasharray');
|
|
78
59
|
if (!dashAttr || dashAttr === 'none') {
|
|
79
60
|
pathEl.style.strokeDasharray = '8 5';
|
|
80
61
|
}
|
|
81
|
-
// One pixel of dashoffset shift per ms feels like a steady current; use
|
|
82
|
-
// `duration` as the ms per cycle of 24px (one base dash repeat-ish).
|
|
83
62
|
const cycle = 24;
|
|
84
63
|
const dir = params.reverse ? 1 : -1;
|
|
85
64
|
function flowStep(now) {
|
|
86
65
|
const elapsed = now - start;
|
|
87
|
-
const offset = (dir * (elapsed / dur) * cycle) % (cycle * 1000);
|
|
66
|
+
const offset = (dir * (elapsed / dur) * cycle) % (cycle * 1000);
|
|
88
67
|
if (pathEl)
|
|
89
68
|
pathEl.style.strokeDashoffset = String(offset);
|
|
90
69
|
raf = requestAnimationFrame(flowStep);
|
|
@@ -92,59 +71,95 @@ export function arrowClipDraw(node, params) {
|
|
|
92
71
|
raf = requestAnimationFrame(flowStep);
|
|
93
72
|
return;
|
|
94
73
|
}
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
74
|
+
// PATH-LENGTH REVEAL for draw / undraw / draw-undraw.
|
|
75
|
+
let len = 0;
|
|
76
|
+
try {
|
|
77
|
+
len = pathEl.getTotalLength();
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
len = 0;
|
|
81
|
+
}
|
|
82
|
+
if (!len) {
|
|
83
|
+
// Degenerate path — nothing sensible to animate.
|
|
84
|
+
restorePath();
|
|
85
|
+
return;
|
|
98
86
|
}
|
|
99
|
-
|
|
100
|
-
|
|
87
|
+
// dashoffset reveals from the path start (positive) or the path end
|
|
88
|
+
// (negative) toward the other end, so `reverse` flips the draw direction.
|
|
89
|
+
const hideOffset = params.reverse ? -len : len;
|
|
90
|
+
// p: 0 = nothing drawn, 1 = fully drawn (along the path).
|
|
91
|
+
function setProgress(p) {
|
|
92
|
+
if (!pathEl)
|
|
93
|
+
return;
|
|
94
|
+
pathEl.style.strokeDasharray = `${len} ${len}`;
|
|
95
|
+
pathEl.style.strokeDashoffset = String(hideOffset * (1 - p));
|
|
96
|
+
}
|
|
97
|
+
// Head only shows once the end of the line is reached.
|
|
98
|
+
function setHead(p) {
|
|
99
|
+
if (headEl)
|
|
100
|
+
headEl.style.opacity = p >= 0.999 ? '1' : '0';
|
|
101
|
+
}
|
|
102
|
+
const cubicOut = (t) => 1 - Math.pow(1 - t, 3);
|
|
103
|
+
// Initial frame.
|
|
104
|
+
if (m === 'undraw') {
|
|
105
|
+
setProgress(1);
|
|
106
|
+
setHead(1);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
setProgress(0);
|
|
110
|
+
setHead(0);
|
|
101
111
|
}
|
|
102
112
|
function step(now) {
|
|
103
113
|
const elapsed = now - start;
|
|
104
114
|
if (m === 'draw') {
|
|
105
115
|
const t = Math.min(elapsed / dur, 1);
|
|
106
|
-
const
|
|
107
|
-
|
|
116
|
+
const p = cubicOut(t);
|
|
117
|
+
setProgress(p);
|
|
118
|
+
setHead(p);
|
|
108
119
|
if (t < 1)
|
|
109
120
|
raf = requestAnimationFrame(step);
|
|
110
121
|
else if (params.loop)
|
|
111
122
|
run();
|
|
112
123
|
else {
|
|
113
|
-
|
|
124
|
+
restorePath();
|
|
114
125
|
raf = 0;
|
|
115
|
-
}
|
|
126
|
+
} // settle fully drawn (restores dashes)
|
|
116
127
|
}
|
|
117
128
|
else if (m === 'undraw') {
|
|
118
129
|
const t = Math.min(elapsed / dur, 1);
|
|
119
|
-
const
|
|
120
|
-
|
|
130
|
+
const p = 1 - cubicOut(t);
|
|
131
|
+
setProgress(p);
|
|
132
|
+
setHead(p);
|
|
121
133
|
if (t < 1)
|
|
122
134
|
raf = requestAnimationFrame(step);
|
|
123
135
|
else if (params.loop)
|
|
124
136
|
run();
|
|
125
137
|
else {
|
|
126
|
-
|
|
138
|
+
setProgress(0);
|
|
139
|
+
setHead(0);
|
|
127
140
|
raf = 0;
|
|
128
|
-
}
|
|
141
|
+
} // settle hidden
|
|
129
142
|
}
|
|
130
143
|
else if (m === 'draw-undraw') {
|
|
131
144
|
const half = dur / 2;
|
|
132
145
|
if (elapsed < half) {
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
146
|
+
const p = cubicOut(Math.min(elapsed / half, 1));
|
|
147
|
+
setProgress(p);
|
|
148
|
+
setHead(p);
|
|
136
149
|
raf = requestAnimationFrame(step);
|
|
137
150
|
}
|
|
138
151
|
else {
|
|
139
152
|
const t = Math.min((elapsed - half) / half, 1);
|
|
140
|
-
const
|
|
141
|
-
|
|
153
|
+
const p = 1 - cubicOut(t);
|
|
154
|
+
setProgress(p);
|
|
155
|
+
setHead(p);
|
|
142
156
|
if (t < 1)
|
|
143
157
|
raf = requestAnimationFrame(step);
|
|
144
158
|
else if (params.loop)
|
|
145
159
|
run();
|
|
146
160
|
else {
|
|
147
|
-
|
|
161
|
+
setProgress(0);
|
|
162
|
+
setHead(0);
|
|
148
163
|
raf = 0;
|
|
149
164
|
}
|
|
150
165
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "animot-presenter",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
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",
|