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