@sarmal/core 0.10.0 → 0.13.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/dist/auto-init.cjs +189 -94
- package/dist/auto-init.cjs.map +1 -1
- package/dist/auto-init.d.cts +1 -2
- package/dist/auto-init.d.ts +1 -2
- package/dist/auto-init.js +188 -93
- package/dist/auto-init.js.map +1 -1
- package/dist/curves/artemis2.cjs +10 -7
- package/dist/curves/artemis2.d.cts +1 -1
- package/dist/curves/artemis2.d.ts +1 -1
- package/dist/curves/artemis2.js +9 -6
- package/dist/curves/astroid.cjs +4 -4
- package/dist/curves/astroid.d.cts +1 -1
- package/dist/curves/astroid.d.ts +1 -1
- package/dist/curves/astroid.js +3 -3
- package/dist/curves/deltoid.cjs +4 -4
- package/dist/curves/deltoid.d.cts +1 -1
- package/dist/curves/deltoid.d.ts +1 -1
- package/dist/curves/deltoid.js +3 -3
- package/dist/curves/epicycloid3.cjs +4 -4
- package/dist/curves/epicycloid3.d.cts +1 -1
- package/dist/curves/epicycloid3.d.ts +1 -1
- package/dist/curves/epicycloid3.js +3 -3
- package/dist/curves/epitrochoid7.cjs +5 -5
- package/dist/curves/epitrochoid7.d.cts +1 -1
- package/dist/curves/epitrochoid7.d.ts +1 -1
- package/dist/curves/epitrochoid7.js +4 -4
- package/dist/curves/index.cjs +32 -28
- package/dist/curves/index.cjs.map +1 -1
- package/dist/curves/index.d.cts +25 -13
- package/dist/curves/index.d.ts +25 -13
- package/dist/curves/index.js +44 -28
- package/dist/curves/index.js.map +1 -1
- package/dist/curves/lame.cjs +6 -5
- package/dist/curves/lame.d.cts +1 -1
- package/dist/curves/lame.d.ts +1 -1
- package/dist/curves/lame.js +5 -4
- package/dist/curves/lissajous32.cjs +4 -4
- package/dist/curves/lissajous32.d.cts +1 -1
- package/dist/curves/lissajous32.d.ts +1 -1
- package/dist/curves/lissajous32.js +3 -3
- package/dist/curves/lissajous43.cjs +4 -4
- package/dist/curves/lissajous43.d.cts +1 -1
- package/dist/curves/lissajous43.d.ts +1 -1
- package/dist/curves/lissajous43.js +3 -3
- package/dist/curves/rose3.cjs +4 -4
- package/dist/curves/rose3.d.cts +1 -1
- package/dist/curves/rose3.d.ts +1 -1
- package/dist/curves/rose3.js +3 -3
- package/dist/curves/rose5.cjs +4 -4
- package/dist/curves/rose5.d.cts +1 -1
- package/dist/curves/rose5.d.ts +1 -1
- package/dist/curves/rose5.js +3 -3
- package/dist/index.cjs +216 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +69 -34
- package/dist/index.d.ts +69 -34
- package/dist/index.js +233 -108
- package/dist/index.js.map +1 -1
- package/dist/types-BW0bpL1Z.d.cts +290 -0
- package/dist/types-BW0bpL1Z.d.ts +290 -0
- package/package.json +1 -1
- package/dist/types-DcyISvnH.d.cts +0 -230
- package/dist/types-DcyISvnH.d.ts +0 -230
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
3
|
// src/engine.ts
|
|
4
4
|
var TWO_PI = Math.PI * 2;
|
|
@@ -63,22 +63,24 @@ function resolveCurve(curveDef) {
|
|
|
63
63
|
period,
|
|
64
64
|
speed,
|
|
65
65
|
skeleton: curveDef.skeleton,
|
|
66
|
-
skeletonFn: curveDef.skeletonFn
|
|
66
|
+
skeletonFn: curveDef.skeletonFn,
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
69
|
function createEngine(curveDef, trailLength = 120) {
|
|
70
70
|
if (!Number.isFinite(trailLength) || trailLength <= 0) {
|
|
71
71
|
throw new RangeError(
|
|
72
|
-
`[sarmal] trailLength must be a positive finite number, got ${trailLength}
|
|
72
|
+
`[sarmal] trailLength must be a positive finite number, got ${trailLength}`,
|
|
73
73
|
);
|
|
74
74
|
}
|
|
75
75
|
let curve = resolveCurve(curveDef);
|
|
76
76
|
const trail = new CircularBuffer(trailLength);
|
|
77
77
|
let t = 0;
|
|
78
78
|
let actualTime = 0;
|
|
79
|
+
let userSpeedOverride = null;
|
|
79
80
|
let morphCurveB = null;
|
|
80
81
|
let _morphAlpha = null;
|
|
81
82
|
let _morphStrategy = "normalized";
|
|
83
|
+
let _speedTransition = null;
|
|
82
84
|
function sampleSkeleton(c, sampleT) {
|
|
83
85
|
if (c.skeletonFn) {
|
|
84
86
|
return c.skeletonFn(sampleT);
|
|
@@ -90,15 +92,25 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
90
92
|
}
|
|
91
93
|
return {
|
|
92
94
|
tick(deltaTime) {
|
|
93
|
-
|
|
95
|
+
if (_speedTransition !== null) {
|
|
96
|
+
_speedTransition.elapsed += deltaTime * 1e3;
|
|
97
|
+
const alpha = Math.min(_speedTransition.elapsed / _speedTransition.duration, 1);
|
|
98
|
+
userSpeedOverride = lerp(_speedTransition.from, _speedTransition.to, alpha);
|
|
99
|
+
if (alpha >= 1) {
|
|
100
|
+
userSpeedOverride = _speedTransition.to;
|
|
101
|
+
_speedTransition.resolve();
|
|
102
|
+
_speedTransition = null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
let effectiveSpeed = userSpeedOverride ?? curve.speed;
|
|
94
106
|
if (morphCurveB !== null && _morphAlpha !== null) {
|
|
95
|
-
effectiveSpeed = lerp(
|
|
107
|
+
effectiveSpeed = lerp(effectiveSpeed, morphCurveB.speed, _morphAlpha);
|
|
96
108
|
}
|
|
97
109
|
t = (t + effectiveSpeed * deltaTime) % curve.period;
|
|
98
110
|
actualTime += deltaTime;
|
|
99
111
|
if (morphCurveB !== null && _morphAlpha !== null) {
|
|
100
112
|
const a = curve.fn(t, actualTime, EMPTY_PARAMS);
|
|
101
|
-
const tB = _morphStrategy === "normalized" ? t / curve.period * morphCurveB.period : t;
|
|
113
|
+
const tB = _morphStrategy === "normalized" ? (t / curve.period) * morphCurveB.period : t;
|
|
102
114
|
const b = morphCurveB.fn(tB, actualTime, EMPTY_PARAMS);
|
|
103
115
|
trail.push(a.x + (b.x - a.x) * _morphAlpha, a.y + (b.y - a.y) * _morphAlpha);
|
|
104
116
|
} else {
|
|
@@ -122,14 +134,14 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
122
134
|
trail.clear();
|
|
123
135
|
},
|
|
124
136
|
jump(newT, { clearTrail = false } = {}) {
|
|
125
|
-
t = (newT % curve.period + curve.period) % curve.period;
|
|
137
|
+
t = ((newT % curve.period) + curve.period) % curve.period;
|
|
126
138
|
if (clearTrail) {
|
|
127
139
|
trail.clear();
|
|
128
140
|
}
|
|
129
141
|
},
|
|
130
142
|
seek(targetT, { wrap = false, step = curve.period / trailLength } = {}) {
|
|
131
143
|
const advance = curve.speed * step;
|
|
132
|
-
const target = (targetT % curve.period + curve.period) % curve.period;
|
|
144
|
+
const target = ((targetT % curve.period) + curve.period) % curve.period;
|
|
133
145
|
const targetTime = target / curve.speed;
|
|
134
146
|
t = target;
|
|
135
147
|
actualTime = targetTime;
|
|
@@ -138,7 +150,7 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
138
150
|
const count = wrap ? trailLength : Math.min(trailLength, pointsFromStart);
|
|
139
151
|
for (let i = count - 1; i >= 0; i--) {
|
|
140
152
|
const sampleT = target - i * advance;
|
|
141
|
-
const wrappedT = (sampleT % curve.period + curve.period) % curve.period;
|
|
153
|
+
const wrappedT = ((sampleT % curve.period) + curve.period) % curve.period;
|
|
142
154
|
const time = targetTime - i * step;
|
|
143
155
|
const point = curve.fn(wrappedT, time, EMPTY_PARAMS);
|
|
144
156
|
trail.push(point.x, point.y);
|
|
@@ -155,13 +167,16 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
155
167
|
...frozenB,
|
|
156
168
|
fn: (sampleT, time, params) => {
|
|
157
169
|
const a = frozenA.fn(sampleT, time, params);
|
|
158
|
-
const tB =
|
|
170
|
+
const tB =
|
|
171
|
+
frozenStrategy === "normalized"
|
|
172
|
+
? (sampleT / frozenA.period) * frozenB.period
|
|
173
|
+
: sampleT;
|
|
159
174
|
const b = frozenB.fn(tB, time, params);
|
|
160
175
|
return {
|
|
161
176
|
x: a.x + (b.x - a.x) * frozenAlpha,
|
|
162
|
-
y: a.y + (b.y - a.y) * frozenAlpha
|
|
177
|
+
y: a.y + (b.y - a.y) * frozenAlpha,
|
|
163
178
|
};
|
|
164
|
-
}
|
|
179
|
+
},
|
|
165
180
|
};
|
|
166
181
|
}
|
|
167
182
|
_morphStrategy = strategy;
|
|
@@ -174,7 +189,7 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
174
189
|
completeMorph() {
|
|
175
190
|
if (morphCurveB !== null) {
|
|
176
191
|
if (_morphStrategy === "normalized" && curve.period !== morphCurveB.period) {
|
|
177
|
-
t = t / curve.period * morphCurveB.period;
|
|
192
|
+
t = (t / curve.period) * morphCurveB.period;
|
|
178
193
|
}
|
|
179
194
|
curve = morphCurveB;
|
|
180
195
|
}
|
|
@@ -186,23 +201,64 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
186
201
|
const points = new Array(steps);
|
|
187
202
|
if (morphCurveB !== null && _morphAlpha !== null) {
|
|
188
203
|
for (let i = 0; i < steps; i++) {
|
|
189
|
-
const sampleT = i / (steps - 1) * curve.period;
|
|
204
|
+
const sampleT = (i / (steps - 1)) * curve.period;
|
|
190
205
|
const a = sampleSkeleton(curve, sampleT);
|
|
191
|
-
const tB =
|
|
206
|
+
const tB =
|
|
207
|
+
_morphStrategy === "normalized"
|
|
208
|
+
? (sampleT / curve.period) * morphCurveB.period
|
|
209
|
+
: sampleT;
|
|
192
210
|
const b = sampleSkeleton(morphCurveB, tB);
|
|
193
211
|
points[i] = {
|
|
194
212
|
x: a.x + (b.x - a.x) * _morphAlpha,
|
|
195
|
-
y: a.y + (b.y - a.y) * _morphAlpha
|
|
213
|
+
y: a.y + (b.y - a.y) * _morphAlpha,
|
|
196
214
|
};
|
|
197
215
|
}
|
|
198
216
|
return points;
|
|
199
217
|
}
|
|
200
218
|
for (let i = 0; i < steps; i++) {
|
|
201
|
-
const sampleT = i / (steps - 1) * curve.period;
|
|
219
|
+
const sampleT = (i / (steps - 1)) * curve.period;
|
|
202
220
|
points[i] = sampleSkeleton(curve, sampleT);
|
|
203
221
|
}
|
|
204
222
|
return points;
|
|
205
|
-
}
|
|
223
|
+
},
|
|
224
|
+
setSpeed(speed) {
|
|
225
|
+
if (!Number.isFinite(speed)) {
|
|
226
|
+
throw new Error("speed must be a finite number");
|
|
227
|
+
}
|
|
228
|
+
if (_speedTransition !== null) {
|
|
229
|
+
_speedTransition.reject(new Error("Speed transition cancelled"));
|
|
230
|
+
_speedTransition = null;
|
|
231
|
+
}
|
|
232
|
+
userSpeedOverride = speed;
|
|
233
|
+
},
|
|
234
|
+
getSpeed() {
|
|
235
|
+
return userSpeedOverride ?? curve.speed;
|
|
236
|
+
},
|
|
237
|
+
resetSpeed() {
|
|
238
|
+
userSpeedOverride = null;
|
|
239
|
+
},
|
|
240
|
+
setSpeedOver(speed, duration) {
|
|
241
|
+
if (!Number.isFinite(speed)) {
|
|
242
|
+
throw new Error("speed must be a finite number");
|
|
243
|
+
}
|
|
244
|
+
if (!Number.isFinite(duration) || duration <= 0) {
|
|
245
|
+
throw new Error("duration must be a finite number greater than 0");
|
|
246
|
+
}
|
|
247
|
+
if (_speedTransition !== null) {
|
|
248
|
+
_speedTransition.reject(new Error("Speed transition cancelled"));
|
|
249
|
+
_speedTransition = null;
|
|
250
|
+
}
|
|
251
|
+
const from = userSpeedOverride ?? curve.speed;
|
|
252
|
+
return new Promise((resolve, reject) => {
|
|
253
|
+
_speedTransition = { from, to: speed, elapsed: 0, duration, resolve, reject };
|
|
254
|
+
});
|
|
255
|
+
},
|
|
256
|
+
cancelSpeedTransition() {
|
|
257
|
+
if (_speedTransition !== null) {
|
|
258
|
+
_speedTransition.reject(new Error("Speed transition cancelled"));
|
|
259
|
+
_speedTransition = null;
|
|
260
|
+
}
|
|
261
|
+
},
|
|
206
262
|
};
|
|
207
263
|
}
|
|
208
264
|
|
|
@@ -210,6 +266,7 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
210
266
|
var DEFAULT_MORPH_DURATION_MS = 300;
|
|
211
267
|
var DEFAULT_SKELETON_OPACITY = 0.15;
|
|
212
268
|
var FIT_PADDING = 0.1;
|
|
269
|
+
var FIT_PADDING_MIN = 4;
|
|
213
270
|
var TRAIL_FADE_CURVE = 1.5;
|
|
214
271
|
var TRAIL_MAX_OPACITY = 0.88;
|
|
215
272
|
var TRAIL_MIN_WIDTH = 0.5;
|
|
@@ -264,13 +321,16 @@ function computeTrailQuad(trail, i, trailCount, toX, toY) {
|
|
|
264
321
|
r1x: nx - n1.x * w1,
|
|
265
322
|
r1y: ny - n1.y * w1,
|
|
266
323
|
opacity,
|
|
267
|
-
progress
|
|
324
|
+
progress,
|
|
268
325
|
};
|
|
269
326
|
}
|
|
270
327
|
function computeBoundaries(pts, logicalWidth, logicalHeight) {
|
|
271
328
|
if (pts.length === 0) return null;
|
|
272
329
|
const first = pts[0];
|
|
273
|
-
let minX = first.x,
|
|
330
|
+
let minX = first.x,
|
|
331
|
+
maxX = first.x,
|
|
332
|
+
minY = first.y,
|
|
333
|
+
maxY = first.y;
|
|
274
334
|
for (const p of pts) {
|
|
275
335
|
if (p.x < minX) {
|
|
276
336
|
minX = p.x;
|
|
@@ -289,21 +349,37 @@ function computeBoundaries(pts, logicalWidth, logicalHeight) {
|
|
|
289
349
|
const h = maxY - minY;
|
|
290
350
|
if (w === 0 && h === 0) {
|
|
291
351
|
throw new Error(
|
|
292
|
-
"[sarmal] Degenerate curve: all skeleton points are identical. Check that your curve fn returns distinct points for different values of t."
|
|
352
|
+
"[sarmal] Degenerate curve: all skeleton points are identical. Check that your curve fn returns distinct points for different values of t.",
|
|
293
353
|
);
|
|
294
354
|
}
|
|
295
|
-
const
|
|
296
|
-
const
|
|
297
|
-
const
|
|
355
|
+
const scaleXProportional = logicalWidth / (w * (1 + FIT_PADDING * 2));
|
|
356
|
+
const scaleYProportional = logicalHeight / (h * (1 + FIT_PADDING * 2));
|
|
357
|
+
const scaleXMinPadding = (logicalWidth - FIT_PADDING_MIN * 2) / w;
|
|
358
|
+
const scaleYMinPadding = (logicalHeight - FIT_PADDING_MIN * 2) / h;
|
|
359
|
+
const scale = Math.min(
|
|
360
|
+
scaleXProportional,
|
|
361
|
+
scaleYProportional,
|
|
362
|
+
scaleXMinPadding,
|
|
363
|
+
scaleYMinPadding,
|
|
364
|
+
);
|
|
298
365
|
return {
|
|
299
366
|
scale,
|
|
300
367
|
offsetX: (logicalWidth - w * scale) / 2 - minX * scale,
|
|
301
|
-
offsetY: (logicalHeight - h * scale) / 2 - minY * scale
|
|
368
|
+
offsetY: (logicalHeight - h * scale) / 2 - minY * scale,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
function enginePassthroughs(engine) {
|
|
372
|
+
return {
|
|
373
|
+
jump: engine.jump,
|
|
374
|
+
seek: engine.seek,
|
|
375
|
+
setSpeed: engine.setSpeed,
|
|
376
|
+
getSpeed: engine.getSpeed,
|
|
377
|
+
resetSpeed: engine.resetSpeed,
|
|
378
|
+
setSpeedOver: engine.setSpeedOver,
|
|
302
379
|
};
|
|
303
380
|
}
|
|
304
381
|
|
|
305
382
|
// src/renderer.ts
|
|
306
|
-
var DEFAULT_HEAD_RADIUS = 4;
|
|
307
383
|
var DEFAULT_SKELETON_COLOR = "#ffffff";
|
|
308
384
|
var GRADIENT = {
|
|
309
385
|
bard: ["#a855f7", "#3b82f6", "#14b8a6", "#ec4899"],
|
|
@@ -311,7 +387,7 @@ var GRADIENT = {
|
|
|
311
387
|
ocean: ["#1e3a8a", "#06b6d4", "#22d3ee", "#e0f2fe"],
|
|
312
388
|
ice: ["#1e3a8a", "#67e8f9"],
|
|
313
389
|
fire: ["#7f1d1d", "#fbbf24"],
|
|
314
|
-
forest: ["#14532d", "#86efac"]
|
|
390
|
+
forest: ["#14532d", "#86efac"],
|
|
315
391
|
};
|
|
316
392
|
var PRESETS = {
|
|
317
393
|
bard: GRADIENT.bard,
|
|
@@ -319,16 +395,16 @@ var PRESETS = {
|
|
|
319
395
|
ocean: GRADIENT.ocean,
|
|
320
396
|
ice: GRADIENT.ice,
|
|
321
397
|
fire: GRADIENT.fire,
|
|
322
|
-
forest: GRADIENT.forest
|
|
398
|
+
forest: GRADIENT.forest,
|
|
323
399
|
};
|
|
324
400
|
function hexToRgb(hex) {
|
|
325
401
|
const n = parseInt(hex.slice(1), 16);
|
|
326
|
-
return { r: n >> 16, g: n >> 8 & 255, b: n & 255 };
|
|
402
|
+
return { r: n >> 16, g: (n >> 8) & 255, b: n & 255 };
|
|
327
403
|
}
|
|
328
404
|
var lerpRgb = (a, b, t) => ({
|
|
329
405
|
r: Math.round(a.r + (b.r - a.r) * t),
|
|
330
406
|
g: Math.round(a.g + (b.g - a.g) * t),
|
|
331
|
-
b: Math.round(a.b + (b.b - a.b) * t)
|
|
407
|
+
b: Math.round(a.b + (b.b - a.b) * t),
|
|
332
408
|
});
|
|
333
409
|
function getPaletteColor(palette, position, timeOffset = 0) {
|
|
334
410
|
if (palette.length === 0) return { r: 255, g: 255, b: 255 };
|
|
@@ -348,7 +424,7 @@ function resolvePalette(palette, trailStyle) {
|
|
|
348
424
|
}
|
|
349
425
|
function hexToRgbComponents(hex) {
|
|
350
426
|
const n = parseInt(hex.slice(1), 16);
|
|
351
|
-
return `${n >> 16},${n >> 8 & 255},${n & 255}`;
|
|
427
|
+
return `${n >> 16},${(n >> 8) & 255},${n & 255}`;
|
|
352
428
|
}
|
|
353
429
|
function applyDprSizing(target, logicalWidth, logicalHeight, dpr) {
|
|
354
430
|
target.style.width = `${logicalWidth}px`;
|
|
@@ -363,14 +439,21 @@ function createRenderer(options) {
|
|
|
363
439
|
}
|
|
364
440
|
const ctx = canvas.getContext("2d");
|
|
365
441
|
const engine = options.engine;
|
|
442
|
+
const trailStyle = options.trailStyle ?? "default";
|
|
443
|
+
const trailColor = options.trailColor ?? "#ffffff";
|
|
444
|
+
const palette = resolvePalette(options.palette, trailStyle);
|
|
445
|
+
function defaultHeadColor() {
|
|
446
|
+
if (trailStyle !== "default") {
|
|
447
|
+
const { r, g, b } = getPaletteColor(palette, 1);
|
|
448
|
+
return `rgb(${r},${g},${b})`;
|
|
449
|
+
}
|
|
450
|
+
return trailColor;
|
|
451
|
+
}
|
|
366
452
|
const opts = {
|
|
367
453
|
skeletonColor: options.skeletonColor ?? DEFAULT_SKELETON_COLOR,
|
|
368
|
-
trailColor
|
|
369
|
-
headColor: options.headColor ??
|
|
370
|
-
headRadius: options.headRadius ?? DEFAULT_HEAD_RADIUS
|
|
454
|
+
trailColor,
|
|
455
|
+
headColor: options.headColor ?? defaultHeadColor(),
|
|
371
456
|
};
|
|
372
|
-
const trailStyle = options.trailStyle ?? "default";
|
|
373
|
-
const palette = resolvePalette(options.palette, trailStyle);
|
|
374
457
|
const trailRgb = hexToRgbComponents(opts.trailColor);
|
|
375
458
|
const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
|
376
459
|
function setupCanvas() {
|
|
@@ -470,7 +553,7 @@ function createRenderer(options) {
|
|
|
470
553
|
i,
|
|
471
554
|
trailCount,
|
|
472
555
|
toX,
|
|
473
|
-
toY
|
|
556
|
+
toY,
|
|
474
557
|
);
|
|
475
558
|
if (trailStyle === "default") {
|
|
476
559
|
ctx.fillStyle = `rgba(${trailRgb},${opacity})`;
|
|
@@ -494,15 +577,14 @@ function createRenderer(options) {
|
|
|
494
577
|
}
|
|
495
578
|
const x = head.x * scale + offsetX;
|
|
496
579
|
const y = head.y * scale + offsetY;
|
|
580
|
+
const r =
|
|
581
|
+
options.headRadius ?? Math.max(2, 3 * Math.sqrt(Math.min(logicalWidth, logicalHeight) / 160));
|
|
497
582
|
ctx.fillStyle = opts.headColor;
|
|
498
583
|
ctx.beginPath();
|
|
499
|
-
ctx.arc(x, y,
|
|
584
|
+
ctx.arc(x, y, r, 0, Math.PI * 2);
|
|
500
585
|
ctx.fill();
|
|
501
586
|
}
|
|
502
|
-
function
|
|
503
|
-
const now = performance.now();
|
|
504
|
-
const deltaTime = Math.min((now - lastTime) / 1e3, 1 / 30);
|
|
505
|
-
lastTime = now;
|
|
587
|
+
function renderFrame(deltaTime) {
|
|
506
588
|
if (trailStyle === "gradient-animated") {
|
|
507
589
|
gradientAnimTime += deltaTime * 1e3;
|
|
508
590
|
}
|
|
@@ -538,27 +620,39 @@ function createRenderer(options) {
|
|
|
538
620
|
drawSkeleton();
|
|
539
621
|
drawTrail();
|
|
540
622
|
drawHead();
|
|
541
|
-
|
|
623
|
+
}
|
|
624
|
+
function loop() {
|
|
625
|
+
const now = performance.now();
|
|
626
|
+
const deltaTime = Math.min((now - lastTime) / 1e3, 1 / 30);
|
|
627
|
+
lastTime = now;
|
|
628
|
+
renderFrame(deltaTime);
|
|
629
|
+
animationId = requestAnimationFrame(loop);
|
|
542
630
|
}
|
|
543
631
|
skeleton = engine.getSarmalSkeleton();
|
|
544
632
|
calculateBoundaries();
|
|
545
633
|
if (!engine.isLiveSkeleton) {
|
|
546
634
|
buildSkeletonCanvas();
|
|
547
635
|
}
|
|
548
|
-
|
|
549
|
-
|
|
636
|
+
if (options.initialT !== void 0) {
|
|
637
|
+
engine.seek(options.initialT);
|
|
638
|
+
}
|
|
639
|
+
renderFrame(0);
|
|
640
|
+
const shouldAutoStart = options.autoStart !== false;
|
|
641
|
+
const instance = {
|
|
642
|
+
play() {
|
|
550
643
|
if (animationId !== null) {
|
|
551
644
|
return;
|
|
552
645
|
}
|
|
553
646
|
lastTime = performance.now();
|
|
554
|
-
|
|
647
|
+
loop();
|
|
555
648
|
},
|
|
556
|
-
|
|
649
|
+
pause() {
|
|
557
650
|
if (animationId === null) {
|
|
558
651
|
return;
|
|
559
652
|
}
|
|
560
653
|
cancelAnimationFrame(animationId);
|
|
561
654
|
animationId = null;
|
|
655
|
+
engine.cancelSpeedTransition();
|
|
562
656
|
},
|
|
563
657
|
reset() {
|
|
564
658
|
engine.reset();
|
|
@@ -571,12 +665,7 @@ function createRenderer(options) {
|
|
|
571
665
|
animationId = null;
|
|
572
666
|
}
|
|
573
667
|
},
|
|
574
|
-
|
|
575
|
-
engine.jump(t, options2);
|
|
576
|
-
},
|
|
577
|
-
seek(t, options2) {
|
|
578
|
-
engine.seek(t, options2);
|
|
579
|
-
},
|
|
668
|
+
...enginePassthroughs(engine),
|
|
580
669
|
morphTo(target, options2) {
|
|
581
670
|
if (morphResolve !== null) {
|
|
582
671
|
engine.completeMorph();
|
|
@@ -590,8 +679,12 @@ function createRenderer(options) {
|
|
|
590
679
|
return new Promise((resolve) => {
|
|
591
680
|
morphResolve = resolve;
|
|
592
681
|
});
|
|
593
|
-
}
|
|
682
|
+
},
|
|
594
683
|
};
|
|
684
|
+
if (shouldAutoStart) {
|
|
685
|
+
instance.play();
|
|
686
|
+
}
|
|
687
|
+
return instance;
|
|
595
688
|
}
|
|
596
689
|
|
|
597
690
|
// src/renderer-svg.ts
|
|
@@ -612,7 +705,7 @@ function sampleCurveSkeleton(curveDef) {
|
|
|
612
705
|
const samples = Math.ceil(period * 50);
|
|
613
706
|
const pts = Array.from({ length: samples });
|
|
614
707
|
for (let i = 0; i < samples; i++) {
|
|
615
|
-
const t = i / (samples - 1) * period;
|
|
708
|
+
const t = (i / (samples - 1)) * period;
|
|
616
709
|
pts[i] = curveDef.skeletonFn ? curveDef.skeletonFn(t) : curveDef.fn(t, 0, EMPTY_PARAMS2);
|
|
617
710
|
}
|
|
618
711
|
return pts;
|
|
@@ -622,16 +715,18 @@ function el(tag) {
|
|
|
622
715
|
}
|
|
623
716
|
function createSVGRenderer(options) {
|
|
624
717
|
const { container, engine } = options;
|
|
718
|
+
const trailColor = options.trailColor ?? "#ffffff";
|
|
625
719
|
const opts = {
|
|
626
720
|
skeletonColor: options.skeletonColor ?? "#ffffff",
|
|
627
|
-
trailColor
|
|
628
|
-
headColor: options.headColor ??
|
|
629
|
-
|
|
630
|
-
ariaLabel: options.ariaLabel ?? "Loading"
|
|
721
|
+
trailColor,
|
|
722
|
+
headColor: options.headColor ?? trailColor,
|
|
723
|
+
ariaLabel: options.ariaLabel ?? "Loading",
|
|
631
724
|
};
|
|
632
725
|
const rect = container.getBoundingClientRect();
|
|
633
726
|
const width = rect.width || 200;
|
|
634
727
|
const height = rect.height || 200;
|
|
728
|
+
const headRadius =
|
|
729
|
+
options.headRadius ?? Math.max(2, 3 * Math.sqrt(Math.min(width, height) / 160));
|
|
635
730
|
const svg = el("svg");
|
|
636
731
|
svg.setAttribute("width", String(width));
|
|
637
732
|
svg.setAttribute("height", String(height));
|
|
@@ -670,7 +765,7 @@ function createSVGRenderer(options) {
|
|
|
670
765
|
}
|
|
671
766
|
const headCircle = el("circle");
|
|
672
767
|
headCircle.setAttribute("fill", opts.headColor);
|
|
673
|
-
headCircle.setAttribute("r", String(
|
|
768
|
+
headCircle.setAttribute("r", String(headRadius));
|
|
674
769
|
svg.appendChild(headCircle);
|
|
675
770
|
container.appendChild(svg);
|
|
676
771
|
let scale = 1;
|
|
@@ -711,7 +806,7 @@ function createSVGRenderer(options) {
|
|
|
711
806
|
i,
|
|
712
807
|
trailCount,
|
|
713
808
|
px,
|
|
714
|
-
py
|
|
809
|
+
py,
|
|
715
810
|
);
|
|
716
811
|
const d = `M${l0x.toFixed(2)} ${l0y.toFixed(2)} L${l1x.toFixed(2)} ${l1y.toFixed(2)} L${r1x.toFixed(2)} ${r1y.toFixed(2)} L${r0x.toFixed(2)} ${r0y.toFixed(2)} Z`;
|
|
717
812
|
trailPaths[i].setAttribute("d", d);
|
|
@@ -733,24 +828,22 @@ function createSVGRenderer(options) {
|
|
|
733
828
|
}
|
|
734
829
|
let animationId = null;
|
|
735
830
|
let lastTime = 0;
|
|
736
|
-
const prefersReducedMotion =
|
|
831
|
+
const prefersReducedMotion =
|
|
832
|
+
typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
737
833
|
let morphResolve = null;
|
|
738
834
|
let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
|
|
739
835
|
let morphTarget = null;
|
|
740
836
|
let morphAlpha = 0;
|
|
741
|
-
function renderFrame() {
|
|
742
|
-
const now = performance.now();
|
|
743
|
-
const dt = Math.min((now - lastTime) / 1e3, 1 / 30);
|
|
744
|
-
lastTime = now;
|
|
837
|
+
function renderFrame(deltaTime) {
|
|
745
838
|
if (engine.morphAlpha !== null) {
|
|
746
|
-
morphAlpha = Math.min(1, morphAlpha +
|
|
839
|
+
morphAlpha = Math.min(1, morphAlpha + deltaTime / (morphDurationMs / 1e3));
|
|
747
840
|
engine.setMorphAlpha(morphAlpha);
|
|
748
841
|
if (morphPathABuilt) {
|
|
749
842
|
skeletonPathA.setAttribute("d", morphPathABuilt);
|
|
750
843
|
skeletonPathA.setAttribute("visibility", "visible");
|
|
751
844
|
skeletonPathA.setAttribute(
|
|
752
845
|
"stroke-opacity",
|
|
753
|
-
String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY)
|
|
846
|
+
String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY),
|
|
754
847
|
);
|
|
755
848
|
}
|
|
756
849
|
if (morphPathBBuilt) {
|
|
@@ -773,7 +866,7 @@ function createSVGRenderer(options) {
|
|
|
773
866
|
updateSkeleton(newSkeleton);
|
|
774
867
|
}
|
|
775
868
|
}
|
|
776
|
-
const trail = engine.tick(
|
|
869
|
+
const trail = engine.tick(deltaTime);
|
|
777
870
|
const trailCount = engine.trailCount;
|
|
778
871
|
if (engine.isLiveSkeleton && engine.morphAlpha === null) {
|
|
779
872
|
const liveSkeleton = engine.getSarmalSkeleton();
|
|
@@ -782,24 +875,36 @@ function createSVGRenderer(options) {
|
|
|
782
875
|
}
|
|
783
876
|
updateTrail(trail, trailCount);
|
|
784
877
|
updateHead(trail, trailCount);
|
|
878
|
+
}
|
|
879
|
+
function loop() {
|
|
880
|
+
const now = performance.now();
|
|
881
|
+
const deltaTime = Math.min((now - lastTime) / 1e3, 1 / 30);
|
|
882
|
+
lastTime = now;
|
|
883
|
+
renderFrame(deltaTime);
|
|
785
884
|
if (!prefersReducedMotion) {
|
|
786
|
-
animationId = requestAnimationFrame(
|
|
885
|
+
animationId = requestAnimationFrame(loop);
|
|
787
886
|
}
|
|
788
887
|
}
|
|
789
|
-
|
|
790
|
-
|
|
888
|
+
if (options.initialT !== void 0) {
|
|
889
|
+
engine.seek(options.initialT);
|
|
890
|
+
}
|
|
891
|
+
renderFrame(0);
|
|
892
|
+
const shouldAutoStart = options.autoStart !== false;
|
|
893
|
+
const instance = {
|
|
894
|
+
play() {
|
|
791
895
|
if (animationId !== null) {
|
|
792
896
|
return;
|
|
793
897
|
}
|
|
794
898
|
lastTime = performance.now();
|
|
795
|
-
|
|
899
|
+
loop();
|
|
796
900
|
},
|
|
797
|
-
|
|
901
|
+
pause() {
|
|
798
902
|
if (animationId === null) {
|
|
799
903
|
return;
|
|
800
904
|
}
|
|
801
905
|
cancelAnimationFrame(animationId);
|
|
802
906
|
animationId = null;
|
|
907
|
+
engine.cancelSpeedTransition();
|
|
803
908
|
},
|
|
804
909
|
reset() {
|
|
805
910
|
engine.reset();
|
|
@@ -811,12 +916,7 @@ function createSVGRenderer(options) {
|
|
|
811
916
|
}
|
|
812
917
|
svg.remove();
|
|
813
918
|
},
|
|
814
|
-
|
|
815
|
-
engine.jump(t, options2);
|
|
816
|
-
},
|
|
817
|
-
seek(t, options2) {
|
|
818
|
-
engine.seek(t, options2);
|
|
819
|
-
},
|
|
919
|
+
...enginePassthroughs(engine),
|
|
820
920
|
morphTo(target, options2) {
|
|
821
921
|
if (morphResolve !== null) {
|
|
822
922
|
engine.completeMorph();
|
|
@@ -839,8 +939,12 @@ function createSVGRenderer(options) {
|
|
|
839
939
|
return new Promise((resolve) => {
|
|
840
940
|
morphResolve = resolve;
|
|
841
941
|
});
|
|
842
|
-
}
|
|
942
|
+
},
|
|
843
943
|
};
|
|
944
|
+
if (shouldAutoStart) {
|
|
945
|
+
instance.play();
|
|
946
|
+
}
|
|
947
|
+
return instance;
|
|
844
948
|
}
|
|
845
949
|
function createSarmalSVG(container, curveDef, options) {
|
|
846
950
|
const { trailLength, ...rendererOpts } = options ?? {};
|
|
@@ -851,19 +955,22 @@ function createSarmalSVG(container, curveDef, options) {
|
|
|
851
955
|
// src/curves/artemis2.ts
|
|
852
956
|
var TWO_PI2 = Math.PI * 2;
|
|
853
957
|
function artemis2Fn(t, _time, _params) {
|
|
854
|
-
const a = 0.35,
|
|
855
|
-
|
|
958
|
+
const a = 0.35,
|
|
959
|
+
b = 0.15,
|
|
960
|
+
ox = 0.175;
|
|
961
|
+
const s = Math.sin(t),
|
|
962
|
+
c = Math.cos(t);
|
|
856
963
|
const denom = 1 + s * s;
|
|
857
964
|
return {
|
|
858
|
-
x: c * (1 + a * c) / denom - ox,
|
|
859
|
-
y: s * c * (1 + b * c) / denom
|
|
965
|
+
x: (c * (1 + a * c)) / denom - ox,
|
|
966
|
+
y: (s * c * (1 + b * c)) / denom,
|
|
860
967
|
};
|
|
861
968
|
}
|
|
862
969
|
var artemis2 = {
|
|
863
970
|
name: "Artemis II",
|
|
864
971
|
fn: artemis2Fn,
|
|
865
972
|
period: TWO_PI2,
|
|
866
|
-
speed: 0.7
|
|
973
|
+
speed: 0.7,
|
|
867
974
|
};
|
|
868
975
|
|
|
869
976
|
// src/curves/astroid.ts
|
|
@@ -873,14 +980,14 @@ function astroidFn(t, _time, _params) {
|
|
|
873
980
|
const s = Math.sin(t);
|
|
874
981
|
return {
|
|
875
982
|
x: c * c * c,
|
|
876
|
-
y: s * s * s
|
|
983
|
+
y: s * s * s,
|
|
877
984
|
};
|
|
878
985
|
}
|
|
879
986
|
var astroid = {
|
|
880
987
|
name: "Astroid",
|
|
881
988
|
fn: astroidFn,
|
|
882
989
|
period: TWO_PI3,
|
|
883
|
-
speed: 1.1
|
|
990
|
+
speed: 1.1,
|
|
884
991
|
};
|
|
885
992
|
|
|
886
993
|
// src/curves/deltoid.ts
|
|
@@ -888,14 +995,14 @@ var TWO_PI4 = Math.PI * 2;
|
|
|
888
995
|
function deltoidFn(t, _time, _params) {
|
|
889
996
|
return {
|
|
890
997
|
x: 2 * Math.cos(t) + Math.cos(2 * t),
|
|
891
|
-
y: 2 * Math.sin(t) - Math.sin(2 * t)
|
|
998
|
+
y: 2 * Math.sin(t) - Math.sin(2 * t),
|
|
892
999
|
};
|
|
893
1000
|
}
|
|
894
1001
|
var deltoid = {
|
|
895
1002
|
name: "Deltoid",
|
|
896
1003
|
fn: deltoidFn,
|
|
897
1004
|
period: TWO_PI4,
|
|
898
|
-
speed: 0.9
|
|
1005
|
+
speed: 0.9,
|
|
899
1006
|
};
|
|
900
1007
|
|
|
901
1008
|
// src/curves/epicycloid3.ts
|
|
@@ -903,14 +1010,14 @@ var TWO_PI5 = Math.PI * 2;
|
|
|
903
1010
|
function epicycloid3Fn(t, _time, _params) {
|
|
904
1011
|
return {
|
|
905
1012
|
x: 4 * Math.cos(t) - Math.cos(4 * t),
|
|
906
|
-
y: 4 * Math.sin(t) - Math.sin(4 * t)
|
|
1013
|
+
y: 4 * Math.sin(t) - Math.sin(4 * t),
|
|
907
1014
|
};
|
|
908
1015
|
}
|
|
909
1016
|
var epicycloid3 = {
|
|
910
1017
|
name: "Epicycloid (n=3)",
|
|
911
1018
|
fn: epicycloid3Fn,
|
|
912
1019
|
period: TWO_PI5,
|
|
913
|
-
speed: 0.75
|
|
1020
|
+
speed: 0.75,
|
|
914
1021
|
};
|
|
915
1022
|
|
|
916
1023
|
// src/curves/epitrochoid7.ts
|
|
@@ -919,14 +1026,14 @@ function epitrochoid7Fn(t, _time, _params) {
|
|
|
919
1026
|
const d = 1 + 0.55 * Math.sin(t * 0.5);
|
|
920
1027
|
return {
|
|
921
1028
|
x: 7 * Math.cos(t) - d * Math.cos(7 * t),
|
|
922
|
-
y: 7 * Math.sin(t) - d * Math.sin(7 * t)
|
|
1029
|
+
y: 7 * Math.sin(t) - d * Math.sin(7 * t),
|
|
923
1030
|
};
|
|
924
1031
|
}
|
|
925
1032
|
function epitrochoid7SkeletonFn(t) {
|
|
926
1033
|
const d = 1.275;
|
|
927
1034
|
return {
|
|
928
1035
|
x: 7 * Math.cos(t) - d * Math.cos(7 * t),
|
|
929
|
-
y: 7 * Math.sin(t) - d * Math.sin(7 * t)
|
|
1036
|
+
y: 7 * Math.sin(t) - d * Math.sin(7 * t),
|
|
930
1037
|
};
|
|
931
1038
|
}
|
|
932
1039
|
var epitrochoid7 = {
|
|
@@ -934,7 +1041,7 @@ var epitrochoid7 = {
|
|
|
934
1041
|
fn: epitrochoid7Fn,
|
|
935
1042
|
period: TWO_PI6,
|
|
936
1043
|
speed: 1.4,
|
|
937
|
-
skeletonFn: epitrochoid7SkeletonFn
|
|
1044
|
+
skeletonFn: epitrochoid7SkeletonFn,
|
|
938
1045
|
};
|
|
939
1046
|
|
|
940
1047
|
// src/curves/lissajous32.ts
|
|
@@ -943,7 +1050,7 @@ function lissajous32Fn(t, time, _params) {
|
|
|
943
1050
|
const phi = time * 0.45;
|
|
944
1051
|
return {
|
|
945
1052
|
x: Math.sin(3 * t + phi),
|
|
946
|
-
y: Math.sin(2 * t)
|
|
1053
|
+
y: Math.sin(2 * t),
|
|
947
1054
|
};
|
|
948
1055
|
}
|
|
949
1056
|
var lissajous32 = {
|
|
@@ -951,7 +1058,7 @@ var lissajous32 = {
|
|
|
951
1058
|
fn: lissajous32Fn,
|
|
952
1059
|
period: TWO_PI7,
|
|
953
1060
|
speed: 2,
|
|
954
|
-
skeleton: "live"
|
|
1061
|
+
skeleton: "live",
|
|
955
1062
|
};
|
|
956
1063
|
|
|
957
1064
|
// src/curves/lissajous43.ts
|
|
@@ -960,7 +1067,7 @@ function lissajous43Fn(t, time, _params) {
|
|
|
960
1067
|
const phi = time * 0.38;
|
|
961
1068
|
return {
|
|
962
1069
|
x: Math.sin(4 * t + phi),
|
|
963
|
-
y: Math.sin(3 * t)
|
|
1070
|
+
y: Math.sin(3 * t),
|
|
964
1071
|
};
|
|
965
1072
|
}
|
|
966
1073
|
var lissajous43 = {
|
|
@@ -968,17 +1075,18 @@ var lissajous43 = {
|
|
|
968
1075
|
fn: lissajous43Fn,
|
|
969
1076
|
period: TWO_PI8,
|
|
970
1077
|
speed: 1.8,
|
|
971
|
-
skeleton: "live"
|
|
1078
|
+
skeleton: "live",
|
|
972
1079
|
};
|
|
973
1080
|
|
|
974
1081
|
// src/curves/lame.ts
|
|
975
1082
|
var TWO_PI9 = Math.PI * 2;
|
|
976
1083
|
function lameFn(t, time, _params) {
|
|
977
1084
|
const p = 1.75 + 1.25 * Math.sin(time * 0.48);
|
|
978
|
-
const c = Math.cos(t),
|
|
1085
|
+
const c = Math.cos(t),
|
|
1086
|
+
s = Math.sin(t);
|
|
979
1087
|
return {
|
|
980
1088
|
x: Math.sign(c) * Math.pow(Math.abs(c), p),
|
|
981
|
-
y: Math.sign(s) * Math.pow(Math.abs(s), p)
|
|
1089
|
+
y: Math.sign(s) * Math.pow(Math.abs(s), p),
|
|
982
1090
|
};
|
|
983
1091
|
}
|
|
984
1092
|
var lame = {
|
|
@@ -986,7 +1094,7 @@ var lame = {
|
|
|
986
1094
|
fn: lameFn,
|
|
987
1095
|
period: TWO_PI9,
|
|
988
1096
|
speed: 1,
|
|
989
|
-
skeleton: "live"
|
|
1097
|
+
skeleton: "live",
|
|
990
1098
|
};
|
|
991
1099
|
|
|
992
1100
|
// src/curves/rose3.ts
|
|
@@ -995,14 +1103,14 @@ function rose3Fn(t, _time, _params) {
|
|
|
995
1103
|
const r = Math.cos(3 * t);
|
|
996
1104
|
return {
|
|
997
1105
|
x: r * Math.cos(t),
|
|
998
|
-
y: r * Math.sin(t)
|
|
1106
|
+
y: r * Math.sin(t),
|
|
999
1107
|
};
|
|
1000
1108
|
}
|
|
1001
1109
|
var rose3 = {
|
|
1002
1110
|
name: "Rose (n=3)",
|
|
1003
1111
|
fn: rose3Fn,
|
|
1004
1112
|
period: TWO_PI10,
|
|
1005
|
-
speed: 1.15
|
|
1113
|
+
speed: 1.15,
|
|
1006
1114
|
};
|
|
1007
1115
|
|
|
1008
1116
|
// src/curves/rose5.ts
|
|
@@ -1011,14 +1119,14 @@ function rose5Fn(t, _time, _params) {
|
|
|
1011
1119
|
const r = Math.cos(5 * t);
|
|
1012
1120
|
return {
|
|
1013
1121
|
x: r * Math.cos(t),
|
|
1014
|
-
y: r * Math.sin(t)
|
|
1122
|
+
y: r * Math.sin(t),
|
|
1015
1123
|
};
|
|
1016
1124
|
}
|
|
1017
1125
|
var rose5 = {
|
|
1018
1126
|
name: "Rose (n=5)",
|
|
1019
1127
|
fn: rose5Fn,
|
|
1020
1128
|
period: TWO_PI11,
|
|
1021
|
-
speed: 1
|
|
1129
|
+
speed: 1,
|
|
1022
1130
|
};
|
|
1023
1131
|
|
|
1024
1132
|
// src/curves/index.ts
|
|
@@ -1032,7 +1140,7 @@ var curves = {
|
|
|
1032
1140
|
lissajous32,
|
|
1033
1141
|
lissajous43,
|
|
1034
1142
|
epicycloid3,
|
|
1035
|
-
lame
|
|
1143
|
+
lame,
|
|
1036
1144
|
};
|
|
1037
1145
|
|
|
1038
1146
|
// src/index.ts
|
|
@@ -1059,4 +1167,4 @@ exports.lissajous43 = lissajous43;
|
|
|
1059
1167
|
exports.rose3 = rose3;
|
|
1060
1168
|
exports.rose5 = rose5;
|
|
1061
1169
|
//# sourceMappingURL=index.cjs.map
|
|
1062
|
-
//# sourceMappingURL=index.cjs.map
|
|
1170
|
+
//# sourceMappingURL=index.cjs.map
|