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