@sarmal/core 0.4.2 → 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.
- package/dist/auto-init.cjs +207 -9
- package/dist/auto-init.cjs.map +1 -1
- package/dist/auto-init.js +207 -9
- package/dist/auto-init.js.map +1 -1
- package/dist/index.cjs +333 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -15
- package/dist/index.d.ts +71 -15
- package/dist/index.js +333 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -44,27 +44,57 @@ var CircularBuffer = class {
|
|
|
44
44
|
return this.count;
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
|
-
function
|
|
48
|
-
|
|
47
|
+
function resolveCurve(curveDef) {
|
|
48
|
+
return {
|
|
49
49
|
name: curveDef.name,
|
|
50
50
|
fn: curveDef.fn,
|
|
51
51
|
period: curveDef.period ?? TWO_PI,
|
|
52
52
|
speed: curveDef.speed ?? 1,
|
|
53
|
+
skeleton: curveDef.skeleton,
|
|
54
|
+
skeletonFn: curveDef.skeletonFn,
|
|
53
55
|
};
|
|
56
|
+
}
|
|
57
|
+
function createEngine(curveDef, trailLength = 120) {
|
|
58
|
+
let curve = resolveCurve(curveDef);
|
|
54
59
|
const trail = new CircularBuffer(trailLength);
|
|
55
60
|
let t = 0;
|
|
56
61
|
let actualTime = 0;
|
|
62
|
+
let morphCurveB = null;
|
|
63
|
+
let _morphAlpha = null;
|
|
64
|
+
let _morphStrategy = "normalized";
|
|
65
|
+
function sampleSkeleton(c, sampleT) {
|
|
66
|
+
if (c.skeletonFn) {
|
|
67
|
+
return c.skeletonFn(sampleT);
|
|
68
|
+
}
|
|
69
|
+
if (c.skeleton === "live") {
|
|
70
|
+
return c.fn(sampleT, actualTime, {});
|
|
71
|
+
}
|
|
72
|
+
return c.fn(sampleT, 0, {});
|
|
73
|
+
}
|
|
57
74
|
return {
|
|
58
75
|
tick(deltaTime) {
|
|
59
76
|
t = (t + curve.speed * deltaTime) % curve.period;
|
|
60
77
|
actualTime += deltaTime;
|
|
61
|
-
|
|
62
|
-
|
|
78
|
+
if (morphCurveB !== null && _morphAlpha !== null) {
|
|
79
|
+
const a = curve.fn(t, actualTime, {});
|
|
80
|
+
const tB = _morphStrategy === "normalized" ? (t / curve.period) * morphCurveB.period : t;
|
|
81
|
+
const b = morphCurveB.fn(tB, actualTime, {});
|
|
82
|
+
trail.push(a.x + (b.x - a.x) * _morphAlpha, a.y + (b.y - a.y) * _morphAlpha);
|
|
83
|
+
} else {
|
|
84
|
+
const point = curve.fn(t, actualTime, {});
|
|
85
|
+
trail.push(point.x, point.y);
|
|
86
|
+
}
|
|
63
87
|
return trail.toArray();
|
|
64
88
|
},
|
|
65
89
|
get trailCount() {
|
|
66
90
|
return trail.length;
|
|
67
91
|
},
|
|
92
|
+
get isLiveSkeleton() {
|
|
93
|
+
return curve.skeleton === "live";
|
|
94
|
+
},
|
|
95
|
+
get morphAlpha() {
|
|
96
|
+
return _morphAlpha;
|
|
97
|
+
},
|
|
68
98
|
reset() {
|
|
69
99
|
t = 0;
|
|
70
100
|
actualTime = 0;
|
|
@@ -93,13 +123,65 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
93
123
|
trail.push(point.x, point.y);
|
|
94
124
|
}
|
|
95
125
|
},
|
|
126
|
+
startMorph(target, strategy = "normalized") {
|
|
127
|
+
const resolvedTarget = resolveCurve(target);
|
|
128
|
+
if (morphCurveB !== null && _morphAlpha !== null) {
|
|
129
|
+
const frozenAlpha = _morphAlpha;
|
|
130
|
+
const frozenA = curve;
|
|
131
|
+
const frozenB = morphCurveB;
|
|
132
|
+
const frozenStrategy = _morphStrategy;
|
|
133
|
+
curve = {
|
|
134
|
+
...frozenB,
|
|
135
|
+
fn: (sampleT, time, params) => {
|
|
136
|
+
const a = frozenA.fn(sampleT, time, params);
|
|
137
|
+
const tB =
|
|
138
|
+
frozenStrategy === "normalized"
|
|
139
|
+
? (sampleT / frozenA.period) * frozenB.period
|
|
140
|
+
: sampleT;
|
|
141
|
+
const b = frozenB.fn(tB, time, params);
|
|
142
|
+
return {
|
|
143
|
+
x: a.x + (b.x - a.x) * frozenAlpha,
|
|
144
|
+
y: a.y + (b.y - a.y) * frozenAlpha,
|
|
145
|
+
};
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
_morphStrategy = strategy;
|
|
150
|
+
morphCurveB = resolvedTarget;
|
|
151
|
+
_morphAlpha = 0;
|
|
152
|
+
},
|
|
153
|
+
setMorphAlpha(alpha) {
|
|
154
|
+
_morphAlpha = alpha;
|
|
155
|
+
},
|
|
156
|
+
completeMorph() {
|
|
157
|
+
if (morphCurveB !== null) {
|
|
158
|
+
curve = morphCurveB;
|
|
159
|
+
}
|
|
160
|
+
morphCurveB = null;
|
|
161
|
+
_morphAlpha = null;
|
|
162
|
+
},
|
|
96
163
|
getSarmalSkeleton() {
|
|
97
164
|
const steps = Math.ceil(curve.period * POINTS_PER_PERIOD_UNIT);
|
|
98
165
|
const points = new Array(steps);
|
|
166
|
+
if (morphCurveB !== null && _morphAlpha !== null) {
|
|
167
|
+
for (let i = 0; i < steps; i++) {
|
|
168
|
+
const sampleT = (i / (steps - 1)) * curve.period;
|
|
169
|
+
const a = sampleSkeleton(curve, sampleT);
|
|
170
|
+
const tB =
|
|
171
|
+
_morphStrategy === "normalized"
|
|
172
|
+
? (sampleT / curve.period) * morphCurveB.period
|
|
173
|
+
: sampleT;
|
|
174
|
+
const b = sampleSkeleton(morphCurveB, tB);
|
|
175
|
+
points[i] = {
|
|
176
|
+
x: a.x + (b.x - a.x) * _morphAlpha,
|
|
177
|
+
y: a.y + (b.y - a.y) * _morphAlpha,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
return points;
|
|
181
|
+
}
|
|
99
182
|
for (let i = 0; i < steps; i++) {
|
|
100
183
|
const sampleT = (i / (steps - 1)) * curve.period;
|
|
101
|
-
|
|
102
|
-
points[i] = point;
|
|
184
|
+
points[i] = sampleSkeleton(curve, sampleT);
|
|
103
185
|
}
|
|
104
186
|
return points;
|
|
105
187
|
},
|
|
@@ -107,6 +189,7 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
107
189
|
}
|
|
108
190
|
|
|
109
191
|
// src/renderer.ts
|
|
192
|
+
var DEFAULT_MORPH_DURATION_MS = 300;
|
|
110
193
|
var DEFAULT_HEAD_RADIUS = 4;
|
|
111
194
|
var DEFAULT_GLOW_SIZE = 20;
|
|
112
195
|
var DEFAULT_SKELETON_COLOR = "#ffffff";
|
|
@@ -149,6 +232,12 @@ function createRenderer(options) {
|
|
|
149
232
|
let offsetY = 0;
|
|
150
233
|
let animationId = null;
|
|
151
234
|
let lastTime = 0;
|
|
235
|
+
let morphResolve = null;
|
|
236
|
+
let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
|
|
237
|
+
let morphTarget = null;
|
|
238
|
+
let morphAlpha = 0;
|
|
239
|
+
let skeletonCanvasA = null;
|
|
240
|
+
let skeletonCanvasB = null;
|
|
152
241
|
function calculateBoundaries() {
|
|
153
242
|
if (skeleton.length === 0) {
|
|
154
243
|
return;
|
|
@@ -200,10 +289,38 @@ function createRenderer(options) {
|
|
|
200
289
|
skeletonCtx.stroke();
|
|
201
290
|
}
|
|
202
291
|
function drawSkeleton() {
|
|
203
|
-
if (
|
|
292
|
+
if (opts.skeletonColor === "transparent") {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (engine.morphAlpha !== null) {
|
|
296
|
+
if (skeletonCanvasA) {
|
|
297
|
+
ctx.globalAlpha = (1 - morphAlpha) * DEFAULT_SKELETON_OPACITY;
|
|
298
|
+
ctx.drawImage(skeletonCanvasA, 0, 0);
|
|
299
|
+
}
|
|
300
|
+
if (skeletonCanvasB) {
|
|
301
|
+
ctx.globalAlpha = morphAlpha * DEFAULT_SKELETON_OPACITY;
|
|
302
|
+
ctx.drawImage(skeletonCanvasB, 0, 0);
|
|
303
|
+
}
|
|
304
|
+
ctx.globalAlpha = 1;
|
|
204
305
|
return;
|
|
205
306
|
}
|
|
206
|
-
|
|
307
|
+
if (engine.isLiveSkeleton) {
|
|
308
|
+
if (skeleton.length < 2) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
ctx.strokeStyle = `rgba(${hexToRgbComponents(opts.skeletonColor)},${DEFAULT_SKELETON_OPACITY})`;
|
|
312
|
+
ctx.lineWidth = 1.5;
|
|
313
|
+
ctx.beginPath();
|
|
314
|
+
const first = skeleton[0];
|
|
315
|
+
ctx.moveTo(first.x * scale + offsetX, first.y * scale + offsetY);
|
|
316
|
+
for (let i = 1; i < skeleton.length; i++) {
|
|
317
|
+
const p = skeleton[i];
|
|
318
|
+
ctx.lineTo(p.x * scale + offsetX, p.y * scale + offsetY);
|
|
319
|
+
}
|
|
320
|
+
ctx.stroke();
|
|
321
|
+
} else if (skeletonCanvas) {
|
|
322
|
+
ctx.drawImage(skeletonCanvas, 0, 0);
|
|
323
|
+
}
|
|
207
324
|
}
|
|
208
325
|
function drawTrail() {
|
|
209
326
|
if (trailCount < 2) {
|
|
@@ -253,10 +370,29 @@ function createRenderer(options) {
|
|
|
253
370
|
const now = performance.now();
|
|
254
371
|
const deltaTime = Math.min((now - lastTime) / 1e3, 1 / 30);
|
|
255
372
|
lastTime = now;
|
|
373
|
+
if (engine.morphAlpha !== null) {
|
|
374
|
+
morphAlpha = Math.min(1, morphAlpha + deltaTime / (morphDurationMs / 1e3));
|
|
375
|
+
engine.setMorphAlpha(morphAlpha);
|
|
376
|
+
skeleton = engine.getSarmalSkeleton();
|
|
377
|
+
calculateBoundaries();
|
|
378
|
+
if (morphAlpha >= 1) {
|
|
379
|
+
engine.completeMorph();
|
|
380
|
+
morphResolve?.();
|
|
381
|
+
morphResolve = null;
|
|
382
|
+
morphTarget = null;
|
|
383
|
+
morphAlpha = 0;
|
|
384
|
+
skeletonCanvasA = null;
|
|
385
|
+
skeletonCanvasB = null;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
256
388
|
trail = engine.tick(deltaTime);
|
|
257
389
|
trailCount = engine.trailCount;
|
|
258
390
|
head = trailCount > 0 ? trail[trailCount - 1] : null;
|
|
259
391
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
392
|
+
if (engine.isLiveSkeleton || engine.morphAlpha !== null) {
|
|
393
|
+
skeleton = engine.getSarmalSkeleton();
|
|
394
|
+
calculateBoundaries();
|
|
395
|
+
}
|
|
260
396
|
drawSkeleton();
|
|
261
397
|
drawTrail();
|
|
262
398
|
drawHead();
|
|
@@ -264,7 +400,9 @@ function createRenderer(options) {
|
|
|
264
400
|
}
|
|
265
401
|
skeleton = engine.getSarmalSkeleton();
|
|
266
402
|
calculateBoundaries();
|
|
267
|
-
|
|
403
|
+
if (!engine.isLiveSkeleton) {
|
|
404
|
+
buildSkeletonCanvas();
|
|
405
|
+
}
|
|
268
406
|
return {
|
|
269
407
|
start() {
|
|
270
408
|
if (animationId !== null) {
|
|
@@ -297,10 +435,60 @@ function createRenderer(options) {
|
|
|
297
435
|
seekWithTrail(t) {
|
|
298
436
|
engine.seekWithTrail(t);
|
|
299
437
|
},
|
|
438
|
+
morphTo(target, options2) {
|
|
439
|
+
if (morphResolve !== null) {
|
|
440
|
+
engine.completeMorph();
|
|
441
|
+
morphResolve();
|
|
442
|
+
morphResolve = null;
|
|
443
|
+
morphAlpha = 0;
|
|
444
|
+
skeletonCanvasA = null;
|
|
445
|
+
skeletonCanvasB = null;
|
|
446
|
+
}
|
|
447
|
+
morphDurationMs = options2?.duration ?? DEFAULT_MORPH_DURATION_MS;
|
|
448
|
+
morphTarget = target;
|
|
449
|
+
morphAlpha = 0;
|
|
450
|
+
const currentSkeleton = engine.getSarmalSkeleton();
|
|
451
|
+
if (currentSkeleton.length >= 2) {
|
|
452
|
+
skeletonCanvasA = new OffscreenCanvas(canvas.width, canvas.height);
|
|
453
|
+
const ctxA = skeletonCanvasA.getContext("2d");
|
|
454
|
+
ctxA.strokeStyle = `rgba(${hexToRgbComponents(opts.skeletonColor)},${DEFAULT_SKELETON_OPACITY})`;
|
|
455
|
+
ctxA.lineWidth = 1.5;
|
|
456
|
+
ctxA.beginPath();
|
|
457
|
+
const first = currentSkeleton[0];
|
|
458
|
+
ctxA.moveTo(first.x * scale + offsetX, first.y * scale + offsetY);
|
|
459
|
+
for (let i = 1; i < currentSkeleton.length; i++) {
|
|
460
|
+
const p = currentSkeleton[i];
|
|
461
|
+
ctxA.lineTo(p.x * scale + offsetX, p.y * scale + offsetY);
|
|
462
|
+
}
|
|
463
|
+
ctxA.stroke();
|
|
464
|
+
}
|
|
465
|
+
engine.startMorph(target, options2?.morphStrategy);
|
|
466
|
+
if (morphTarget && !engine.isLiveSkeleton) {
|
|
467
|
+
skeletonCanvasB = new OffscreenCanvas(canvas.width, canvas.height);
|
|
468
|
+
const skeletonCtx = skeletonCanvasB.getContext("2d");
|
|
469
|
+
skeletonCtx.strokeStyle = `rgba(${hexToRgbComponents(opts.skeletonColor)},${DEFAULT_SKELETON_OPACITY})`;
|
|
470
|
+
skeletonCtx.lineWidth = 1.5;
|
|
471
|
+
skeletonCtx.beginPath();
|
|
472
|
+
const period = morphTarget.period ?? Math.PI * 2;
|
|
473
|
+
const samples = Math.max(50, Math.round(period * 20));
|
|
474
|
+
const firstB = morphTarget.fn(0, 0, {});
|
|
475
|
+
skeletonCtx.moveTo(firstB.x * scale + offsetX, firstB.y * scale + offsetY);
|
|
476
|
+
for (let i = 1; i <= samples; i++) {
|
|
477
|
+
const t = (i / samples) * period;
|
|
478
|
+
const p = morphTarget.fn(t, 0, {});
|
|
479
|
+
skeletonCtx.lineTo(p.x * scale + offsetX, p.y * scale + offsetY);
|
|
480
|
+
}
|
|
481
|
+
skeletonCtx.stroke();
|
|
482
|
+
}
|
|
483
|
+
return new Promise((resolve) => {
|
|
484
|
+
morphResolve = resolve;
|
|
485
|
+
});
|
|
486
|
+
},
|
|
300
487
|
};
|
|
301
488
|
}
|
|
302
489
|
|
|
303
490
|
// src/renderer-svg.ts
|
|
491
|
+
var DEFAULT_MORPH_DURATION_MS2 = 300;
|
|
304
492
|
var TRAIL_BATCH_COUNT = 12;
|
|
305
493
|
var TRAIL_FADE_CURVE2 = 1.5;
|
|
306
494
|
var TRAIL_MAX_OPACITY2 = 0.88;
|
|
@@ -365,6 +553,20 @@ function createSVGRenderer(options) {
|
|
|
365
553
|
skeletonPath.setAttribute("stroke-opacity", String(DEFAULT_SKELETON_OPACITY2));
|
|
366
554
|
skeletonPath.setAttribute("stroke-width", "1.5");
|
|
367
555
|
svg.appendChild(skeletonPath);
|
|
556
|
+
const skeletonPathA = el("path");
|
|
557
|
+
skeletonPathA.setAttribute("fill", "none");
|
|
558
|
+
skeletonPathA.setAttribute("stroke", opts.skeletonColor);
|
|
559
|
+
skeletonPathA.setAttribute("stroke-width", "1.5");
|
|
560
|
+
skeletonPathA.setAttribute("visibility", "hidden");
|
|
561
|
+
svg.appendChild(skeletonPathA);
|
|
562
|
+
const skeletonPathB = el("path");
|
|
563
|
+
skeletonPathB.setAttribute("fill", "none");
|
|
564
|
+
skeletonPathB.setAttribute("stroke", opts.skeletonColor);
|
|
565
|
+
skeletonPathB.setAttribute("stroke-width", "1.5");
|
|
566
|
+
skeletonPathB.setAttribute("visibility", "hidden");
|
|
567
|
+
svg.appendChild(skeletonPathB);
|
|
568
|
+
let morphPathABuilt = "";
|
|
569
|
+
let morphPathBBuilt = "";
|
|
368
570
|
const trailPaths = [];
|
|
369
571
|
for (let i = 0; i < TRAIL_BATCH_COUNT; i++) {
|
|
370
572
|
const path = el("path");
|
|
@@ -424,16 +626,23 @@ function createSVGRenderer(options) {
|
|
|
424
626
|
function py(p) {
|
|
425
627
|
return (p.y * scale + offsetY).toFixed(2);
|
|
426
628
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
629
|
+
function updateSkeleton(skeleton2) {
|
|
630
|
+
if (skeleton2.length < 2) {
|
|
631
|
+
skeletonPath.setAttribute("d", "");
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
let d = `M${px(skeleton2[0])} ${py(skeleton2[0])}`;
|
|
635
|
+
for (let i = 1; i < skeleton2.length; i++) {
|
|
636
|
+
d += ` L${px(skeleton2[i])} ${py(skeleton2[i])}`;
|
|
433
637
|
}
|
|
434
638
|
d += " Z";
|
|
435
639
|
skeletonPath.setAttribute("d", d);
|
|
436
640
|
}
|
|
641
|
+
const skeleton = engine.getSarmalSkeleton();
|
|
642
|
+
calculateBoundaries(skeleton);
|
|
643
|
+
if (!engine.isLiveSkeleton) {
|
|
644
|
+
updateSkeleton(skeleton);
|
|
645
|
+
}
|
|
437
646
|
function updateTrail(trail, trailCount) {
|
|
438
647
|
if (trailCount < 2) {
|
|
439
648
|
for (const p of trailPaths) {
|
|
@@ -477,12 +686,78 @@ function createSVGRenderer(options) {
|
|
|
477
686
|
let lastTime = 0;
|
|
478
687
|
const prefersReducedMotion =
|
|
479
688
|
typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
689
|
+
let morphResolve = null;
|
|
690
|
+
let morphDurationMs = DEFAULT_MORPH_DURATION_MS2;
|
|
691
|
+
let morphTarget = null;
|
|
692
|
+
let morphAlpha = 0;
|
|
693
|
+
function buildSkeletonPath(target, scale2, offsetX2, offsetY2) {
|
|
694
|
+
const period = target.period ?? Math.PI * 2;
|
|
695
|
+
const samples = Math.max(50, Math.round(period * 20));
|
|
696
|
+
const points = [];
|
|
697
|
+
for (let i = 0; i <= samples; i++) {
|
|
698
|
+
const t = (i / samples) * period;
|
|
699
|
+
const p = target.fn(t, 0, {});
|
|
700
|
+
points.push(p);
|
|
701
|
+
}
|
|
702
|
+
if (points.length < 2) {
|
|
703
|
+
return "";
|
|
704
|
+
}
|
|
705
|
+
const px2 = (p) => (p.x * scale2 + offsetX2).toFixed(2);
|
|
706
|
+
const py2 = (p) => (p.y * scale2 + offsetY2).toFixed(2);
|
|
707
|
+
let d = `M${px2(points[0])} ${py2(points[0])}`;
|
|
708
|
+
for (let i = 1; i < points.length; i++) {
|
|
709
|
+
d += ` L${px2(points[i])} ${py2(points[i])}`;
|
|
710
|
+
}
|
|
711
|
+
d += " Z";
|
|
712
|
+
return d;
|
|
713
|
+
}
|
|
480
714
|
function renderFrame() {
|
|
481
715
|
const now = performance.now();
|
|
482
716
|
const dt = Math.min((now - lastTime) / 1e3, 1 / 30);
|
|
483
717
|
lastTime = now;
|
|
718
|
+
if (engine.morphAlpha !== null) {
|
|
719
|
+
morphAlpha = Math.min(1, morphAlpha + dt / (morphDurationMs / 1e3));
|
|
720
|
+
engine.setMorphAlpha(morphAlpha);
|
|
721
|
+
const morphSkeleton = engine.getSarmalSkeleton();
|
|
722
|
+
calculateBoundaries(morphSkeleton);
|
|
723
|
+
if (!engine.isLiveSkeleton) {
|
|
724
|
+
updateSkeleton(morphSkeleton);
|
|
725
|
+
}
|
|
726
|
+
if (morphPathABuilt) {
|
|
727
|
+
skeletonPathA.setAttribute("d", morphPathABuilt);
|
|
728
|
+
skeletonPathA.setAttribute("visibility", "visible");
|
|
729
|
+
skeletonPathA.setAttribute(
|
|
730
|
+
"stroke-opacity",
|
|
731
|
+
String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY2),
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
if (morphPathBBuilt) {
|
|
735
|
+
skeletonPathB.setAttribute("d", morphPathBBuilt);
|
|
736
|
+
skeletonPathB.setAttribute("visibility", "visible");
|
|
737
|
+
skeletonPathB.setAttribute(
|
|
738
|
+
"stroke-opacity",
|
|
739
|
+
String(morphAlpha * DEFAULT_SKELETON_OPACITY2),
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
if (morphAlpha >= 1) {
|
|
743
|
+
engine.completeMorph();
|
|
744
|
+
morphResolve?.();
|
|
745
|
+
morphResolve = null;
|
|
746
|
+
morphTarget = null;
|
|
747
|
+
morphAlpha = 0;
|
|
748
|
+
morphPathABuilt = "";
|
|
749
|
+
morphPathBBuilt = "";
|
|
750
|
+
skeletonPathA.setAttribute("visibility", "hidden");
|
|
751
|
+
skeletonPathB.setAttribute("visibility", "hidden");
|
|
752
|
+
}
|
|
753
|
+
}
|
|
484
754
|
const trail = engine.tick(dt);
|
|
485
755
|
const trailCount = engine.trailCount;
|
|
756
|
+
if (engine.isLiveSkeleton) {
|
|
757
|
+
const liveSkeleton = engine.getSarmalSkeleton();
|
|
758
|
+
calculateBoundaries(liveSkeleton);
|
|
759
|
+
updateSkeleton(liveSkeleton);
|
|
760
|
+
}
|
|
486
761
|
updateTrail(trail, trailCount);
|
|
487
762
|
updateHead(trail, trailCount);
|
|
488
763
|
if (!prefersReducedMotion) {
|
|
@@ -520,6 +795,38 @@ function createSVGRenderer(options) {
|
|
|
520
795
|
seekWithTrail(t) {
|
|
521
796
|
engine.seekWithTrail(t);
|
|
522
797
|
},
|
|
798
|
+
morphTo(target, options2) {
|
|
799
|
+
if (morphResolve !== null) {
|
|
800
|
+
engine.completeMorph();
|
|
801
|
+
morphResolve();
|
|
802
|
+
morphResolve = null;
|
|
803
|
+
morphAlpha = 0;
|
|
804
|
+
skeletonPathA.setAttribute("visibility", "hidden");
|
|
805
|
+
skeletonPathB.setAttribute("visibility", "hidden");
|
|
806
|
+
}
|
|
807
|
+
morphDurationMs = options2?.duration ?? DEFAULT_MORPH_DURATION_MS2;
|
|
808
|
+
morphTarget = target;
|
|
809
|
+
morphAlpha = 0;
|
|
810
|
+
const currentSkeleton = engine.getSarmalSkeleton();
|
|
811
|
+
if (currentSkeleton.length >= 2) {
|
|
812
|
+
const px2 = (p) => (p.x * scale + offsetX).toFixed(2);
|
|
813
|
+
const py2 = (p) => (p.y * scale + offsetY).toFixed(2);
|
|
814
|
+
morphPathABuilt = `M${px2(currentSkeleton[0])} ${py2(currentSkeleton[0])}`;
|
|
815
|
+
for (let i = 1; i < currentSkeleton.length; i++) {
|
|
816
|
+
morphPathABuilt += ` L${px2(currentSkeleton[i])} ${py2(currentSkeleton[i])}`;
|
|
817
|
+
}
|
|
818
|
+
morphPathABuilt += " Z";
|
|
819
|
+
} else {
|
|
820
|
+
morphPathABuilt = "";
|
|
821
|
+
}
|
|
822
|
+
engine.startMorph(target, options2?.morphStrategy);
|
|
823
|
+
if (morphTarget) {
|
|
824
|
+
morphPathBBuilt = buildSkeletonPath(morphTarget, scale, offsetX, offsetY);
|
|
825
|
+
}
|
|
826
|
+
return new Promise((resolve) => {
|
|
827
|
+
morphResolve = resolve;
|
|
828
|
+
});
|
|
829
|
+
},
|
|
523
830
|
};
|
|
524
831
|
}
|
|
525
832
|
function createSarmalSVG(container, curveDef, options) {
|
|
@@ -549,6 +856,13 @@ function epitrochoid7(t, _time, _params) {
|
|
|
549
856
|
y: 7 * Math.sin(t) - d * Math.sin(7 * t),
|
|
550
857
|
};
|
|
551
858
|
}
|
|
859
|
+
function epitrochoid7Skeleton(t) {
|
|
860
|
+
const d = 1.275;
|
|
861
|
+
return {
|
|
862
|
+
x: 7 * Math.cos(t) - d * Math.cos(7 * t),
|
|
863
|
+
y: 7 * Math.sin(t) - d * Math.sin(7 * t),
|
|
864
|
+
};
|
|
865
|
+
}
|
|
552
866
|
function astroid(t, _time, _params) {
|
|
553
867
|
const c = Math.cos(t);
|
|
554
868
|
const s = Math.sin(t);
|
|
@@ -618,6 +932,7 @@ var curves = {
|
|
|
618
932
|
fn: epitrochoid7,
|
|
619
933
|
period: TWO_PI2,
|
|
620
934
|
speed: 1.4,
|
|
935
|
+
skeletonFn: epitrochoid7Skeleton,
|
|
621
936
|
},
|
|
622
937
|
astroid: {
|
|
623
938
|
name: "Astroid",
|
|
@@ -648,12 +963,14 @@ var curves = {
|
|
|
648
963
|
fn: lissajous32,
|
|
649
964
|
period: TWO_PI2,
|
|
650
965
|
speed: 2,
|
|
966
|
+
skeleton: "live",
|
|
651
967
|
},
|
|
652
968
|
lissajous43: {
|
|
653
969
|
name: "Lissajous 4:3",
|
|
654
970
|
fn: lissajous43,
|
|
655
971
|
period: TWO_PI2,
|
|
656
972
|
speed: 1.8,
|
|
973
|
+
skeleton: "live",
|
|
657
974
|
},
|
|
658
975
|
epicycloid3: {
|
|
659
976
|
name: "Epicycloid (n=3)",
|
|
@@ -666,6 +983,7 @@ var curves = {
|
|
|
666
983
|
fn: lame,
|
|
667
984
|
period: TWO_PI2,
|
|
668
985
|
speed: 1,
|
|
986
|
+
skeleton: "live",
|
|
669
987
|
},
|
|
670
988
|
};
|
|
671
989
|
|