@sarmal/core 0.5.0 → 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 +216 -60
- 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 +215 -59
- package/dist/auto-init.js.map +1 -1
- package/dist/index.cjs +326 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +228 -163
- package/dist/index.d.ts +228 -163
- package/dist/index.js +325 -57
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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;
|
|
@@ -44,24 +44,46 @@ 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
53
|
skeleton: curveDef.skeleton,
|
|
54
|
-
skeletonFn: curveDef.skeletonFn
|
|
54
|
+
skeletonFn: curveDef.skeletonFn,
|
|
55
55
|
};
|
|
56
|
+
}
|
|
57
|
+
function createEngine(curveDef, trailLength = 120) {
|
|
58
|
+
let curve = resolveCurve(curveDef);
|
|
56
59
|
const trail = new CircularBuffer(trailLength);
|
|
57
60
|
let t = 0;
|
|
58
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
|
+
}
|
|
59
74
|
return {
|
|
60
75
|
tick(deltaTime) {
|
|
61
76
|
t = (t + curve.speed * deltaTime) % curve.period;
|
|
62
77
|
actualTime += deltaTime;
|
|
63
|
-
|
|
64
|
-
|
|
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
|
+
}
|
|
65
87
|
return trail.toArray();
|
|
66
88
|
},
|
|
67
89
|
get trailCount() {
|
|
@@ -70,20 +92,23 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
70
92
|
get isLiveSkeleton() {
|
|
71
93
|
return curve.skeleton === "live";
|
|
72
94
|
},
|
|
95
|
+
get morphAlpha() {
|
|
96
|
+
return _morphAlpha;
|
|
97
|
+
},
|
|
73
98
|
reset() {
|
|
74
99
|
t = 0;
|
|
75
100
|
actualTime = 0;
|
|
76
101
|
trail.clear();
|
|
77
102
|
},
|
|
78
103
|
seek(newT, { clearTrail = false } = {}) {
|
|
79
|
-
t = (newT % curve.period + curve.period) % curve.period;
|
|
104
|
+
t = ((newT % curve.period) + curve.period) % curve.period;
|
|
80
105
|
if (clearTrail) {
|
|
81
106
|
trail.clear();
|
|
82
107
|
}
|
|
83
108
|
},
|
|
84
109
|
seekWithTrail(targetT, { wrap = false, step = curve.period / trailLength } = {}) {
|
|
85
110
|
const advance = curve.speed * step;
|
|
86
|
-
const target = (targetT % curve.period + curve.period) % curve.period;
|
|
111
|
+
const target = ((targetT % curve.period) + curve.period) % curve.period;
|
|
87
112
|
const targetTime = target / curve.speed;
|
|
88
113
|
t = target;
|
|
89
114
|
actualTime = targetTime;
|
|
@@ -98,31 +123,73 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
98
123
|
trail.push(point.x, point.y);
|
|
99
124
|
}
|
|
100
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
|
+
},
|
|
101
163
|
getSarmalSkeleton() {
|
|
102
164
|
const steps = Math.ceil(curve.period * POINTS_PER_PERIOD_UNIT);
|
|
103
165
|
const points = new Array(steps);
|
|
104
|
-
if (
|
|
166
|
+
if (morphCurveB !== null && _morphAlpha !== null) {
|
|
105
167
|
for (let i = 0; i < steps; i++) {
|
|
106
|
-
const sampleT = i / (steps - 1) * curve.period;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
points[i] = curve.fn(sampleT, 0, {});
|
|
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
|
+
};
|
|
118
179
|
}
|
|
180
|
+
return points;
|
|
181
|
+
}
|
|
182
|
+
for (let i = 0; i < steps; i++) {
|
|
183
|
+
const sampleT = (i / (steps - 1)) * curve.period;
|
|
184
|
+
points[i] = sampleSkeleton(curve, sampleT);
|
|
119
185
|
}
|
|
120
186
|
return points;
|
|
121
|
-
}
|
|
187
|
+
},
|
|
122
188
|
};
|
|
123
189
|
}
|
|
124
190
|
|
|
125
191
|
// src/renderer.ts
|
|
192
|
+
var DEFAULT_MORPH_DURATION_MS = 300;
|
|
126
193
|
var DEFAULT_HEAD_RADIUS = 4;
|
|
127
194
|
var DEFAULT_GLOW_SIZE = 20;
|
|
128
195
|
var DEFAULT_SKELETON_COLOR = "#ffffff";
|
|
@@ -137,7 +204,7 @@ var GLOW_INNER_EDGE = 0.4;
|
|
|
137
204
|
var GLOW_FALLOFF_OPACITY = 0.53;
|
|
138
205
|
function hexToRgbComponents(hex) {
|
|
139
206
|
const n = parseInt(hex.slice(1), 16);
|
|
140
|
-
return `${n >> 16},${n >> 8 & 255},${n & 255}`;
|
|
207
|
+
return `${n >> 16},${(n >> 8) & 255},${n & 255}`;
|
|
141
208
|
}
|
|
142
209
|
function createRenderer(options) {
|
|
143
210
|
const canvas = options.canvas;
|
|
@@ -151,7 +218,7 @@ function createRenderer(options) {
|
|
|
151
218
|
trailColor: options.trailColor ?? "#ffffff",
|
|
152
219
|
headColor: options.headColor ?? "#ffffff",
|
|
153
220
|
headRadius: options.headRadius ?? DEFAULT_HEAD_RADIUS,
|
|
154
|
-
glowSize: options.glowSize ?? DEFAULT_GLOW_SIZE
|
|
221
|
+
glowSize: options.glowSize ?? DEFAULT_GLOW_SIZE,
|
|
155
222
|
};
|
|
156
223
|
const trailRgb = hexToRgbComponents(opts.trailColor);
|
|
157
224
|
const headRgbFalloff = `rgba(${hexToRgbComponents(opts.headColor)},${GLOW_FALLOFF_OPACITY})`;
|
|
@@ -165,12 +232,21 @@ function createRenderer(options) {
|
|
|
165
232
|
let offsetY = 0;
|
|
166
233
|
let animationId = null;
|
|
167
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;
|
|
168
241
|
function calculateBoundaries() {
|
|
169
242
|
if (skeleton.length === 0) {
|
|
170
243
|
return;
|
|
171
244
|
}
|
|
172
245
|
const first = skeleton[0];
|
|
173
|
-
let minX = first.x,
|
|
246
|
+
let minX = first.x,
|
|
247
|
+
maxX = first.x,
|
|
248
|
+
minY = first.y,
|
|
249
|
+
maxY = first.y;
|
|
174
250
|
for (const p of skeleton) {
|
|
175
251
|
if (p.x < minX) {
|
|
176
252
|
minX = p.x;
|
|
@@ -216,6 +292,18 @@ function createRenderer(options) {
|
|
|
216
292
|
if (opts.skeletonColor === "transparent") {
|
|
217
293
|
return;
|
|
218
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;
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
219
307
|
if (engine.isLiveSkeleton) {
|
|
220
308
|
if (skeleton.length < 2) {
|
|
221
309
|
return;
|
|
@@ -282,11 +370,26 @@ function createRenderer(options) {
|
|
|
282
370
|
const now = performance.now();
|
|
283
371
|
const deltaTime = Math.min((now - lastTime) / 1e3, 1 / 30);
|
|
284
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
|
+
}
|
|
285
388
|
trail = engine.tick(deltaTime);
|
|
286
389
|
trailCount = engine.trailCount;
|
|
287
390
|
head = trailCount > 0 ? trail[trailCount - 1] : null;
|
|
288
391
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
289
|
-
if (engine.isLiveSkeleton) {
|
|
392
|
+
if (engine.isLiveSkeleton || engine.morphAlpha !== null) {
|
|
290
393
|
skeleton = engine.getSarmalSkeleton();
|
|
291
394
|
calculateBoundaries();
|
|
292
395
|
}
|
|
@@ -331,11 +434,61 @@ function createRenderer(options) {
|
|
|
331
434
|
},
|
|
332
435
|
seekWithTrail(t) {
|
|
333
436
|
engine.seekWithTrail(t);
|
|
334
|
-
}
|
|
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
|
+
},
|
|
335
487
|
};
|
|
336
488
|
}
|
|
337
489
|
|
|
338
490
|
// src/renderer-svg.ts
|
|
491
|
+
var DEFAULT_MORPH_DURATION_MS2 = 300;
|
|
339
492
|
var TRAIL_BATCH_COUNT = 12;
|
|
340
493
|
var TRAIL_FADE_CURVE2 = 1.5;
|
|
341
494
|
var TRAIL_MAX_OPACITY2 = 0.88;
|
|
@@ -357,7 +510,7 @@ function createSVGRenderer(options) {
|
|
|
357
510
|
headColor: options.headColor ?? "#ffffff",
|
|
358
511
|
headRadius: options.headRadius ?? 4,
|
|
359
512
|
glowSize: options.glowSize ?? 20,
|
|
360
|
-
ariaLabel: options.ariaLabel ?? "Loading"
|
|
513
|
+
ariaLabel: options.ariaLabel ?? "Loading",
|
|
361
514
|
};
|
|
362
515
|
const uid = ++instanceCount;
|
|
363
516
|
const gradientId = `sarmal-glow-${uid}`;
|
|
@@ -400,6 +553,20 @@ function createSVGRenderer(options) {
|
|
|
400
553
|
skeletonPath.setAttribute("stroke-opacity", String(DEFAULT_SKELETON_OPACITY2));
|
|
401
554
|
skeletonPath.setAttribute("stroke-width", "1.5");
|
|
402
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 = "";
|
|
403
570
|
const trailPaths = [];
|
|
404
571
|
for (let i = 0; i < TRAIL_BATCH_COUNT; i++) {
|
|
405
572
|
const path = el("path");
|
|
@@ -427,7 +594,10 @@ function createSVGRenderer(options) {
|
|
|
427
594
|
return;
|
|
428
595
|
}
|
|
429
596
|
const first = skeleton2[0];
|
|
430
|
-
let minX = first.x,
|
|
597
|
+
let minX = first.x,
|
|
598
|
+
maxX = first.x,
|
|
599
|
+
minY = first.y,
|
|
600
|
+
maxY = first.y;
|
|
431
601
|
for (const p of skeleton2) {
|
|
432
602
|
if (p.x < minX) {
|
|
433
603
|
minX = p.x;
|
|
@@ -514,11 +684,73 @@ function createSVGRenderer(options) {
|
|
|
514
684
|
}
|
|
515
685
|
let animationId = null;
|
|
516
686
|
let lastTime = 0;
|
|
517
|
-
const prefersReducedMotion =
|
|
687
|
+
const prefersReducedMotion =
|
|
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
|
+
}
|
|
518
714
|
function renderFrame() {
|
|
519
715
|
const now = performance.now();
|
|
520
716
|
const dt = Math.min((now - lastTime) / 1e3, 1 / 30);
|
|
521
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
|
+
}
|
|
522
754
|
const trail = engine.tick(dt);
|
|
523
755
|
const trailCount = engine.trailCount;
|
|
524
756
|
if (engine.isLiveSkeleton) {
|
|
@@ -562,7 +794,39 @@ function createSVGRenderer(options) {
|
|
|
562
794
|
},
|
|
563
795
|
seekWithTrail(t) {
|
|
564
796
|
engine.seekWithTrail(t);
|
|
565
|
-
}
|
|
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
|
+
},
|
|
566
830
|
};
|
|
567
831
|
}
|
|
568
832
|
function createSarmalSVG(container, curveDef, options) {
|
|
@@ -574,26 +838,29 @@ function createSarmalSVG(container, curveDef, options) {
|
|
|
574
838
|
// src/curves.ts
|
|
575
839
|
var TWO_PI2 = Math.PI * 2;
|
|
576
840
|
function artemis2(t, _time, _params) {
|
|
577
|
-
const a = 0.35,
|
|
578
|
-
|
|
841
|
+
const a = 0.35,
|
|
842
|
+
b = 0.15,
|
|
843
|
+
ox = 0.175;
|
|
844
|
+
const s = Math.sin(t),
|
|
845
|
+
c = Math.cos(t);
|
|
579
846
|
const denom = 1 + s * s;
|
|
580
847
|
return {
|
|
581
|
-
x: c * (1 + a * c) / denom - ox,
|
|
582
|
-
y: s * c * (1 + b * c) / denom
|
|
848
|
+
x: (c * (1 + a * c)) / denom - ox,
|
|
849
|
+
y: (s * c * (1 + b * c)) / denom,
|
|
583
850
|
};
|
|
584
851
|
}
|
|
585
852
|
function epitrochoid7(t, _time, _params) {
|
|
586
853
|
const d = 1 + 0.55 * Math.sin(t * 0.5);
|
|
587
854
|
return {
|
|
588
855
|
x: 7 * Math.cos(t) - d * Math.cos(7 * t),
|
|
589
|
-
y: 7 * Math.sin(t) - d * Math.sin(7 * t)
|
|
856
|
+
y: 7 * Math.sin(t) - d * Math.sin(7 * t),
|
|
590
857
|
};
|
|
591
858
|
}
|
|
592
859
|
function epitrochoid7Skeleton(t) {
|
|
593
860
|
const d = 1.275;
|
|
594
861
|
return {
|
|
595
862
|
x: 7 * Math.cos(t) - d * Math.cos(7 * t),
|
|
596
|
-
y: 7 * Math.sin(t) - d * Math.sin(7 * t)
|
|
863
|
+
y: 7 * Math.sin(t) - d * Math.sin(7 * t),
|
|
597
864
|
};
|
|
598
865
|
}
|
|
599
866
|
function astroid(t, _time, _params) {
|
|
@@ -601,55 +868,56 @@ function astroid(t, _time, _params) {
|
|
|
601
868
|
const s = Math.sin(t);
|
|
602
869
|
return {
|
|
603
870
|
x: c * c * c,
|
|
604
|
-
y: s * s * s
|
|
871
|
+
y: s * s * s,
|
|
605
872
|
};
|
|
606
873
|
}
|
|
607
874
|
function deltoid(t, _time, _params) {
|
|
608
875
|
return {
|
|
609
876
|
x: 2 * Math.cos(t) + Math.cos(2 * t),
|
|
610
|
-
y: 2 * Math.sin(t) - Math.sin(2 * t)
|
|
877
|
+
y: 2 * Math.sin(t) - Math.sin(2 * t),
|
|
611
878
|
};
|
|
612
879
|
}
|
|
613
880
|
function rose5(t, _time, _params) {
|
|
614
881
|
const r = Math.cos(5 * t);
|
|
615
882
|
return {
|
|
616
883
|
x: r * Math.cos(t),
|
|
617
|
-
y: r * Math.sin(t)
|
|
884
|
+
y: r * Math.sin(t),
|
|
618
885
|
};
|
|
619
886
|
}
|
|
620
887
|
function rose3(t, _time, _params) {
|
|
621
888
|
const r = Math.cos(3 * t);
|
|
622
889
|
return {
|
|
623
890
|
x: r * Math.cos(t),
|
|
624
|
-
y: r * Math.sin(t)
|
|
891
|
+
y: r * Math.sin(t),
|
|
625
892
|
};
|
|
626
893
|
}
|
|
627
894
|
function lissajous32(t, time, _params) {
|
|
628
895
|
const phi = time * 0.45;
|
|
629
896
|
return {
|
|
630
897
|
x: Math.sin(3 * t + phi),
|
|
631
|
-
y: Math.sin(2 * t)
|
|
898
|
+
y: Math.sin(2 * t),
|
|
632
899
|
};
|
|
633
900
|
}
|
|
634
901
|
function lissajous43(t, time, _params) {
|
|
635
902
|
const phi = time * 0.38;
|
|
636
903
|
return {
|
|
637
904
|
x: Math.sin(4 * t + phi),
|
|
638
|
-
y: Math.sin(3 * t)
|
|
905
|
+
y: Math.sin(3 * t),
|
|
639
906
|
};
|
|
640
907
|
}
|
|
641
908
|
function epicycloid3(t, _time, _params) {
|
|
642
909
|
return {
|
|
643
910
|
x: 4 * Math.cos(t) - Math.cos(4 * t),
|
|
644
|
-
y: 4 * Math.sin(t) - Math.sin(4 * t)
|
|
911
|
+
y: 4 * Math.sin(t) - Math.sin(4 * t),
|
|
645
912
|
};
|
|
646
913
|
}
|
|
647
914
|
function lame(t, time, _params) {
|
|
648
915
|
const p = 1.75 + 1.25 * Math.sin(time * 0.48);
|
|
649
|
-
const c = Math.cos(t),
|
|
916
|
+
const c = Math.cos(t),
|
|
917
|
+
s = Math.sin(t);
|
|
650
918
|
return {
|
|
651
919
|
x: Math.sign(c) * Math.pow(Math.abs(c), p),
|
|
652
|
-
y: Math.sign(s) * Math.pow(Math.abs(s), p)
|
|
920
|
+
y: Math.sign(s) * Math.pow(Math.abs(s), p),
|
|
653
921
|
};
|
|
654
922
|
}
|
|
655
923
|
var curves = {
|
|
@@ -657,66 +925,66 @@ var curves = {
|
|
|
657
925
|
name: "Artemis II",
|
|
658
926
|
fn: artemis2,
|
|
659
927
|
period: TWO_PI2,
|
|
660
|
-
speed: 0.7
|
|
928
|
+
speed: 0.7,
|
|
661
929
|
},
|
|
662
930
|
epitrochoid7: {
|
|
663
931
|
name: "Epitrochoid",
|
|
664
932
|
fn: epitrochoid7,
|
|
665
933
|
period: TWO_PI2,
|
|
666
934
|
speed: 1.4,
|
|
667
|
-
skeletonFn: epitrochoid7Skeleton
|
|
935
|
+
skeletonFn: epitrochoid7Skeleton,
|
|
668
936
|
},
|
|
669
937
|
astroid: {
|
|
670
938
|
name: "Astroid",
|
|
671
939
|
fn: astroid,
|
|
672
940
|
period: TWO_PI2,
|
|
673
|
-
speed: 1.1
|
|
941
|
+
speed: 1.1,
|
|
674
942
|
},
|
|
675
943
|
deltoid: {
|
|
676
944
|
name: "Deltoid",
|
|
677
945
|
fn: deltoid,
|
|
678
946
|
period: TWO_PI2,
|
|
679
|
-
speed: 0.9
|
|
947
|
+
speed: 0.9,
|
|
680
948
|
},
|
|
681
949
|
rose5: {
|
|
682
950
|
name: "Rose (n=5)",
|
|
683
951
|
fn: rose5,
|
|
684
952
|
period: TWO_PI2,
|
|
685
|
-
speed: 1
|
|
953
|
+
speed: 1,
|
|
686
954
|
},
|
|
687
955
|
rose3: {
|
|
688
956
|
name: "Rose (n=3)",
|
|
689
957
|
fn: rose3,
|
|
690
958
|
period: TWO_PI2,
|
|
691
|
-
speed: 1.15
|
|
959
|
+
speed: 1.15,
|
|
692
960
|
},
|
|
693
961
|
lissajous32: {
|
|
694
962
|
name: "Lissajous 3:2",
|
|
695
963
|
fn: lissajous32,
|
|
696
964
|
period: TWO_PI2,
|
|
697
965
|
speed: 2,
|
|
698
|
-
skeleton: "live"
|
|
966
|
+
skeleton: "live",
|
|
699
967
|
},
|
|
700
968
|
lissajous43: {
|
|
701
969
|
name: "Lissajous 4:3",
|
|
702
970
|
fn: lissajous43,
|
|
703
971
|
period: TWO_PI2,
|
|
704
972
|
speed: 1.8,
|
|
705
|
-
skeleton: "live"
|
|
973
|
+
skeleton: "live",
|
|
706
974
|
},
|
|
707
975
|
epicycloid3: {
|
|
708
976
|
name: "Epicycloid (n=3)",
|
|
709
977
|
fn: epicycloid3,
|
|
710
978
|
period: TWO_PI2,
|
|
711
|
-
speed: 0.75
|
|
979
|
+
speed: 0.75,
|
|
712
980
|
},
|
|
713
981
|
lame: {
|
|
714
982
|
name: "Lam\xE9 Curve",
|
|
715
983
|
fn: lame,
|
|
716
984
|
period: TWO_PI2,
|
|
717
985
|
speed: 1,
|
|
718
|
-
skeleton: "live"
|
|
719
|
-
}
|
|
986
|
+
skeleton: "live",
|
|
987
|
+
},
|
|
720
988
|
};
|
|
721
989
|
|
|
722
990
|
// src/index.ts
|
|
@@ -733,4 +1001,4 @@ exports.createSarmal = createSarmal;
|
|
|
733
1001
|
exports.createSarmalSVG = createSarmalSVG;
|
|
734
1002
|
exports.curves = curves;
|
|
735
1003
|
//# sourceMappingURL=index.cjs.map
|
|
736
|
-
//# sourceMappingURL=index.cjs.map
|
|
1004
|
+
//# sourceMappingURL=index.cjs.map
|