astro-magic-move 0.1.2 → 0.2.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.
- package/package.json +1 -1
- package/src/MagicMove.astro +57 -24
- package/src/types.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-magic-move",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Animated code morphing for Astro, powered by Shiki Magic Move. Build-time tokenization, zero-framework client JS, CSS-variable theming.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/MagicMove.astro
CHANGED
|
@@ -76,14 +76,30 @@ const stepsJson = JSON.stringify(compiledSteps);
|
|
|
76
76
|
|
|
77
77
|
class MagicMoveElement extends HTMLElement {
|
|
78
78
|
private renderer: MagicMoveRenderer | null = null;
|
|
79
|
-
private
|
|
80
|
-
private
|
|
79
|
+
private _steps: any[] = [];
|
|
80
|
+
private _currentStep = 0;
|
|
81
|
+
private _animating = false;
|
|
82
|
+
private _pendingStep: number | null = null;
|
|
81
83
|
private observer: IntersectionObserver | null = null;
|
|
82
84
|
|
|
85
|
+
get step(): number {
|
|
86
|
+
return this._currentStep;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
set step(value: number) {
|
|
90
|
+
const idx = Number(value);
|
|
91
|
+
if (Number.isNaN(idx)) return;
|
|
92
|
+
this.animateToStep(idx);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get totalSteps(): number {
|
|
96
|
+
return this._steps.length;
|
|
97
|
+
}
|
|
98
|
+
|
|
83
99
|
connectedCallback() {
|
|
84
100
|
const dataScript = this.querySelector('script[type="application/json"]');
|
|
85
101
|
if (!dataScript?.textContent) return;
|
|
86
|
-
this.
|
|
102
|
+
this._steps = JSON.parse(dataScript.textContent);
|
|
87
103
|
dataScript.remove();
|
|
88
104
|
|
|
89
105
|
const container = this.querySelector("pre");
|
|
@@ -104,10 +120,10 @@ const stepsJson = JSON.stringify(compiledSteps);
|
|
|
104
120
|
});
|
|
105
121
|
|
|
106
122
|
if (trigger === "auto") {
|
|
107
|
-
this.
|
|
123
|
+
this._currentStep = -1;
|
|
108
124
|
} else {
|
|
109
|
-
this.renderer.replace(this.
|
|
110
|
-
this.
|
|
125
|
+
this.renderer.replace(this._steps[0]);
|
|
126
|
+
this._currentStep = 0;
|
|
111
127
|
}
|
|
112
128
|
this.setupTrigger(trigger, threshold);
|
|
113
129
|
}
|
|
@@ -124,13 +140,15 @@ const stepsJson = JSON.stringify(compiledSteps);
|
|
|
124
140
|
case "click":
|
|
125
141
|
this.style.cursor = "pointer";
|
|
126
142
|
this.addEventListener("click", () => {
|
|
127
|
-
const next = (this.
|
|
143
|
+
const next = (this._currentStep + 1) % this._steps.length;
|
|
128
144
|
this.animateToStep(next);
|
|
129
145
|
});
|
|
130
146
|
break;
|
|
131
147
|
case "auto":
|
|
132
148
|
requestAnimationFrame(() => this.autoPlay());
|
|
133
149
|
break;
|
|
150
|
+
case "none":
|
|
151
|
+
break;
|
|
134
152
|
}
|
|
135
153
|
}
|
|
136
154
|
|
|
@@ -148,28 +166,43 @@ const stepsJson = JSON.stringify(compiledSteps);
|
|
|
148
166
|
}
|
|
149
167
|
|
|
150
168
|
private async autoPlay() {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
169
|
+
const duration = Number(this.dataset.duration ?? 800);
|
|
170
|
+
for (let i = 0; i < this._steps.length; i++) {
|
|
171
|
+
await this.animateToStep(i);
|
|
172
|
+
if (i < this._steps.length - 1) {
|
|
173
|
+
await new Promise((r) => setTimeout(r, duration + 200));
|
|
157
174
|
}
|
|
158
175
|
}
|
|
176
|
+
}
|
|
159
177
|
|
|
160
|
-
|
|
161
|
-
if (!this.renderer || stepIndex === this.
|
|
162
|
-
if (stepIndex < 0 || stepIndex >= this.
|
|
178
|
+
private async animateToStep(stepIndex: number) {
|
|
179
|
+
if (!this.renderer || stepIndex === this._currentStep) return;
|
|
180
|
+
if (stepIndex < 0 || stepIndex >= this._steps.length) return;
|
|
163
181
|
|
|
164
|
-
|
|
165
|
-
|
|
182
|
+
if (this._animating) {
|
|
183
|
+
this._pendingStep = stepIndex;
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
166
186
|
|
|
167
|
-
this.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
187
|
+
this._animating = true;
|
|
188
|
+
try {
|
|
189
|
+
await this.renderer.render(this._steps[stepIndex]);
|
|
190
|
+
this._currentStep = stepIndex;
|
|
191
|
+
this.dispatchEvent(
|
|
192
|
+
new CustomEvent("magic-move:step", {
|
|
193
|
+
detail: { step: stepIndex, total: this._steps.length },
|
|
194
|
+
bubbles: true,
|
|
195
|
+
}),
|
|
196
|
+
);
|
|
197
|
+
} finally {
|
|
198
|
+
this._animating = false;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (this._pendingStep !== null) {
|
|
202
|
+
const next = this._pendingStep;
|
|
203
|
+
this._pendingStep = null;
|
|
204
|
+
this.animateToStep(next);
|
|
205
|
+
}
|
|
173
206
|
}
|
|
174
207
|
}
|
|
175
208
|
|
package/src/types.ts
CHANGED
|
@@ -20,8 +20,9 @@ export interface MagicMoveProps {
|
|
|
20
20
|
* - `'scroll'`: animate when element enters viewport (default)
|
|
21
21
|
* - `'click'`: toggle forward on click, wraps around
|
|
22
22
|
* - `'auto'`: animate immediately when the element mounts
|
|
23
|
+
* - `'none'`: no built-in trigger; control steps externally via the element's `step` property
|
|
23
24
|
*/
|
|
24
|
-
trigger?: "scroll" | "click" | "auto";
|
|
25
|
+
trigger?: "scroll" | "click" | "auto" | "none";
|
|
25
26
|
|
|
26
27
|
/** Animation duration in ms. Default: `800` */
|
|
27
28
|
duration?: number;
|