@sarmal/core 0.7.1 → 0.8.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/LICENSE +9 -0
- package/README.md +89 -3
- package/dist/auto-init.cjs +139 -89
- package/dist/auto-init.cjs.map +1 -1
- package/dist/auto-init.js +138 -88
- package/dist/auto-init.js.map +1 -1
- package/dist/index.cjs +175 -146
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +174 -145
- package/dist/index.js.map +1 -1
- package/package.json +12 -2
- package/dist/auto-init.d.cts +0 -2
- package/dist/auto-init.d.ts +0 -2
- package/dist/index.d.cts +0 -262
- package/dist/index.d.ts +0 -262
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;
|
|
@@ -51,7 +51,7 @@ function resolveCurve(curveDef) {
|
|
|
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
56
|
}
|
|
57
57
|
function createEngine(curveDef, trailLength = 120) {
|
|
@@ -77,7 +77,7 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
77
77
|
actualTime += deltaTime;
|
|
78
78
|
if (morphCurveB !== null && _morphAlpha !== null) {
|
|
79
79
|
const a = curve.fn(t, actualTime, {});
|
|
80
|
-
const tB = _morphStrategy === "normalized" ? t / curve.period * morphCurveB.period : t;
|
|
80
|
+
const tB = _morphStrategy === "normalized" ? (t / curve.period) * morphCurveB.period : t;
|
|
81
81
|
const b = morphCurveB.fn(tB, actualTime, {});
|
|
82
82
|
trail.push(a.x + (b.x - a.x) * _morphAlpha, a.y + (b.y - a.y) * _morphAlpha);
|
|
83
83
|
} else {
|
|
@@ -101,14 +101,14 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
101
101
|
trail.clear();
|
|
102
102
|
},
|
|
103
103
|
seek(newT, { clearTrail = false } = {}) {
|
|
104
|
-
t = (newT % curve.period + curve.period) % curve.period;
|
|
104
|
+
t = ((newT % curve.period) + curve.period) % curve.period;
|
|
105
105
|
if (clearTrail) {
|
|
106
106
|
trail.clear();
|
|
107
107
|
}
|
|
108
108
|
},
|
|
109
109
|
seekWithTrail(targetT, { wrap = false, step = curve.period / trailLength } = {}) {
|
|
110
110
|
const advance = curve.speed * step;
|
|
111
|
-
const target = (targetT % curve.period + curve.period) % curve.period;
|
|
111
|
+
const target = ((targetT % curve.period) + curve.period) % curve.period;
|
|
112
112
|
const targetTime = target / curve.speed;
|
|
113
113
|
t = target;
|
|
114
114
|
actualTime = targetTime;
|
|
@@ -134,13 +134,16 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
134
134
|
...frozenB,
|
|
135
135
|
fn: (sampleT, time, params) => {
|
|
136
136
|
const a = frozenA.fn(sampleT, time, params);
|
|
137
|
-
const tB =
|
|
137
|
+
const tB =
|
|
138
|
+
frozenStrategy === "normalized"
|
|
139
|
+
? (sampleT / frozenA.period) * frozenB.period
|
|
140
|
+
: sampleT;
|
|
138
141
|
const b = frozenB.fn(tB, time, params);
|
|
139
142
|
return {
|
|
140
143
|
x: a.x + (b.x - a.x) * frozenAlpha,
|
|
141
|
-
y: a.y + (b.y - a.y) * frozenAlpha
|
|
144
|
+
y: a.y + (b.y - a.y) * frozenAlpha,
|
|
142
145
|
};
|
|
143
|
-
}
|
|
146
|
+
},
|
|
144
147
|
};
|
|
145
148
|
}
|
|
146
149
|
_morphStrategy = strategy;
|
|
@@ -153,7 +156,7 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
153
156
|
completeMorph() {
|
|
154
157
|
if (morphCurveB !== null) {
|
|
155
158
|
if (_morphStrategy === "normalized" && curve.period !== morphCurveB.period) {
|
|
156
|
-
t = t / curve.period * morphCurveB.period;
|
|
159
|
+
t = (t / curve.period) * morphCurveB.period;
|
|
157
160
|
}
|
|
158
161
|
curve = morphCurveB;
|
|
159
162
|
}
|
|
@@ -165,43 +168,74 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
165
168
|
const points = new Array(steps);
|
|
166
169
|
if (morphCurveB !== null && _morphAlpha !== null) {
|
|
167
170
|
for (let i = 0; i < steps; i++) {
|
|
168
|
-
const sampleT = i / (steps - 1) * curve.period;
|
|
171
|
+
const sampleT = (i / (steps - 1)) * curve.period;
|
|
169
172
|
const a = sampleSkeleton(curve, sampleT);
|
|
170
|
-
const tB =
|
|
173
|
+
const tB =
|
|
174
|
+
_morphStrategy === "normalized"
|
|
175
|
+
? (sampleT / curve.period) * morphCurveB.period
|
|
176
|
+
: sampleT;
|
|
171
177
|
const b = sampleSkeleton(morphCurveB, tB);
|
|
172
178
|
points[i] = {
|
|
173
179
|
x: a.x + (b.x - a.x) * _morphAlpha,
|
|
174
|
-
y: a.y + (b.y - a.y) * _morphAlpha
|
|
180
|
+
y: a.y + (b.y - a.y) * _morphAlpha,
|
|
175
181
|
};
|
|
176
182
|
}
|
|
177
183
|
return points;
|
|
178
184
|
}
|
|
179
185
|
for (let i = 0; i < steps; i++) {
|
|
180
|
-
const sampleT = i / (steps - 1) * curve.period;
|
|
186
|
+
const sampleT = (i / (steps - 1)) * curve.period;
|
|
181
187
|
points[i] = sampleSkeleton(curve, sampleT);
|
|
182
188
|
}
|
|
183
189
|
return points;
|
|
184
|
-
}
|
|
190
|
+
},
|
|
185
191
|
};
|
|
186
192
|
}
|
|
187
193
|
|
|
188
194
|
// src/renderer.ts
|
|
189
195
|
var DEFAULT_MORPH_DURATION_MS = 300;
|
|
190
196
|
var DEFAULT_HEAD_RADIUS = 4;
|
|
191
|
-
var DEFAULT_GLOW_SIZE = 20;
|
|
192
197
|
var DEFAULT_SKELETON_COLOR = "#ffffff";
|
|
193
198
|
var DEFAULT_SKELETON_OPACITY = 0.15;
|
|
194
199
|
var FIT_PADDING = 0.1;
|
|
195
|
-
var TRAIL_BATCH_SIZE = 20;
|
|
196
200
|
var TRAIL_FADE_CURVE = 1.5;
|
|
197
201
|
var TRAIL_MAX_OPACITY = 0.88;
|
|
198
202
|
var TRAIL_MIN_WIDTH = 0.5;
|
|
199
203
|
var TRAIL_MAX_WIDTH = 2.5;
|
|
200
|
-
var GLOW_INNER_EDGE = 0.4;
|
|
201
|
-
var GLOW_FALLOFF_OPACITY = 0.53;
|
|
202
204
|
function hexToRgbComponents(hex) {
|
|
203
205
|
const n = parseInt(hex.slice(1), 16);
|
|
204
|
-
return `${n >> 16},${n >> 8 & 255},${n & 255}`;
|
|
206
|
+
return `${n >> 16},${(n >> 8) & 255},${n & 255}`;
|
|
207
|
+
}
|
|
208
|
+
function computeTangent(trail, i) {
|
|
209
|
+
const count = trail.length;
|
|
210
|
+
if (count < 2) {
|
|
211
|
+
return { x: 1, y: 0 };
|
|
212
|
+
}
|
|
213
|
+
if (i === 0) {
|
|
214
|
+
const dx2 = trail[1].x - trail[0].x;
|
|
215
|
+
const dy2 = trail[1].y - trail[0].y;
|
|
216
|
+
const len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2) || 1;
|
|
217
|
+
return { x: dx2 / len2, y: dy2 / len2 };
|
|
218
|
+
}
|
|
219
|
+
if (i === count - 1) {
|
|
220
|
+
const dx2 = trail[count - 1].x - trail[count - 2].x;
|
|
221
|
+
const dy2 = trail[count - 1].y - trail[count - 2].y;
|
|
222
|
+
const len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2) || 1;
|
|
223
|
+
return { x: dx2 / len2, y: dy2 / len2 };
|
|
224
|
+
}
|
|
225
|
+
const dx = trail[i + 1].x - trail[i - 1].x;
|
|
226
|
+
const dy = trail[i + 1].y - trail[i - 1].y;
|
|
227
|
+
const len = Math.sqrt(dx * dx + dy * dy) || 1;
|
|
228
|
+
return { x: dx / len, y: dy / len };
|
|
229
|
+
}
|
|
230
|
+
function computeNormal(trail, i) {
|
|
231
|
+
const tangent = computeTangent(trail, i);
|
|
232
|
+
return { x: -tangent.y, y: tangent.x };
|
|
233
|
+
}
|
|
234
|
+
function applyDprSizing(target, logicalWidth, logicalHeight, dpr) {
|
|
235
|
+
target.style.width = `${logicalWidth}px`;
|
|
236
|
+
target.style.height = `${logicalHeight}px`;
|
|
237
|
+
target.width = logicalWidth * dpr;
|
|
238
|
+
target.height = logicalHeight * dpr;
|
|
205
239
|
}
|
|
206
240
|
function createRenderer(options) {
|
|
207
241
|
const canvas = options.canvas;
|
|
@@ -215,10 +249,19 @@ function createRenderer(options) {
|
|
|
215
249
|
trailColor: options.trailColor ?? "#ffffff",
|
|
216
250
|
headColor: options.headColor ?? "#ffffff",
|
|
217
251
|
headRadius: options.headRadius ?? DEFAULT_HEAD_RADIUS,
|
|
218
|
-
glowSize: options.glowSize ?? DEFAULT_GLOW_SIZE
|
|
219
252
|
};
|
|
220
253
|
const trailRgb = hexToRgbComponents(opts.trailColor);
|
|
221
|
-
const
|
|
254
|
+
const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
|
255
|
+
function setupCanvas() {
|
|
256
|
+
const rect = canvas.getBoundingClientRect();
|
|
257
|
+
const lw = rect.width || 200;
|
|
258
|
+
const lh = rect.height || 200;
|
|
259
|
+
applyDprSizing(canvas, lw, lh, dpr);
|
|
260
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
261
|
+
}
|
|
262
|
+
setupCanvas();
|
|
263
|
+
let logicalWidth = canvas.width / dpr;
|
|
264
|
+
let logicalHeight = canvas.height / dpr;
|
|
222
265
|
let skeleton = [];
|
|
223
266
|
let skeletonCanvas = null;
|
|
224
267
|
let trail = [];
|
|
@@ -235,7 +278,10 @@ function createRenderer(options) {
|
|
|
235
278
|
function computeBoundaries(pts) {
|
|
236
279
|
if (pts.length === 0) return null;
|
|
237
280
|
const first = pts[0];
|
|
238
|
-
let minX = first.x,
|
|
281
|
+
let minX = first.x,
|
|
282
|
+
maxX = first.x,
|
|
283
|
+
minY = first.y,
|
|
284
|
+
maxY = first.y;
|
|
239
285
|
for (const p of pts) {
|
|
240
286
|
if (p.x < minX) minX = p.x;
|
|
241
287
|
if (p.x > maxX) maxX = p.x;
|
|
@@ -244,17 +290,15 @@ function createRenderer(options) {
|
|
|
244
290
|
}
|
|
245
291
|
const width = maxX - minX;
|
|
246
292
|
const height = maxY - minY;
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
const scaleX = canvasWidth / (width * (1 + FIT_PADDING * 2));
|
|
250
|
-
const scaleY = canvasHeight / (height * (1 + FIT_PADDING * 2));
|
|
293
|
+
const scaleX = logicalWidth / (width * (1 + FIT_PADDING * 2));
|
|
294
|
+
const scaleY = logicalHeight / (height * (1 + FIT_PADDING * 2));
|
|
251
295
|
const s = Math.min(scaleX, scaleY);
|
|
252
296
|
const boundsWidth = width * s;
|
|
253
297
|
const boundsHeight = height * s;
|
|
254
298
|
return {
|
|
255
299
|
scale: s,
|
|
256
|
-
offsetX: (
|
|
257
|
-
offsetY: (
|
|
300
|
+
offsetX: (logicalWidth - boundsWidth) / 2 - minX * s,
|
|
301
|
+
offsetY: (logicalHeight - boundsHeight) / 2 - minY * s,
|
|
258
302
|
};
|
|
259
303
|
}
|
|
260
304
|
function calculateBoundaries() {
|
|
@@ -269,6 +313,7 @@ function createRenderer(options) {
|
|
|
269
313
|
if (skeleton.length < 2) return;
|
|
270
314
|
skeletonCanvas = new OffscreenCanvas(canvas.width, canvas.height);
|
|
271
315
|
const skeletonCtx = skeletonCanvas.getContext("2d");
|
|
316
|
+
skeletonCtx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
272
317
|
skeletonCtx.strokeStyle = `rgba(${hexToRgbComponents(opts.skeletonColor)},${DEFAULT_SKELETON_OPACITY})`;
|
|
273
318
|
skeletonCtx.lineWidth = 1.5;
|
|
274
319
|
skeletonCtx.beginPath();
|
|
@@ -314,32 +359,41 @@ function createRenderer(options) {
|
|
|
314
359
|
}
|
|
315
360
|
ctx.stroke();
|
|
316
361
|
} else if (skeletonCanvas) {
|
|
317
|
-
ctx.drawImage(skeletonCanvas, 0, 0);
|
|
362
|
+
ctx.drawImage(skeletonCanvas, 0, 0, logicalWidth, logicalHeight);
|
|
318
363
|
}
|
|
319
364
|
}
|
|
320
365
|
function drawTrail() {
|
|
321
366
|
if (trailCount < 2) {
|
|
322
367
|
return;
|
|
323
368
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
const bEnd = Math.min(batchIndex + TRAIL_BATCH_SIZE, trailCount - 1);
|
|
328
|
-
const progress = (batchIndex + bEnd) / 2 / (trailCount - 1);
|
|
369
|
+
for (let i = 0; i < trailCount - 1; i++) {
|
|
370
|
+
const progress = i / (trailCount - 1);
|
|
371
|
+
const nextProgress = (i + 1) / (trailCount - 1);
|
|
329
372
|
const alpha = Math.pow(progress, TRAIL_FADE_CURVE) * TRAIL_MAX_OPACITY;
|
|
330
|
-
const
|
|
373
|
+
const width = TRAIL_MIN_WIDTH + progress * (TRAIL_MAX_WIDTH - TRAIL_MIN_WIDTH);
|
|
374
|
+
const nextWidth = TRAIL_MIN_WIDTH + nextProgress * (TRAIL_MAX_WIDTH - TRAIL_MIN_WIDTH);
|
|
375
|
+
const curr = trail[i];
|
|
376
|
+
const next = trail[i + 1];
|
|
377
|
+
const n0 = computeNormal(trail, i);
|
|
378
|
+
const n1 = computeNormal(trail, i + 1);
|
|
379
|
+
const halfW0 = width / 2;
|
|
380
|
+
const halfW1 = nextWidth / 2;
|
|
381
|
+
const l0x = curr.x * scale + offsetX + n0.x * halfW0;
|
|
382
|
+
const l0y = curr.y * scale + offsetY + n0.y * halfW0;
|
|
383
|
+
const r0x = curr.x * scale + offsetX - n0.x * halfW0;
|
|
384
|
+
const r0y = curr.y * scale + offsetY - n0.y * halfW0;
|
|
385
|
+
const l1x = next.x * scale + offsetX + n1.x * halfW1;
|
|
386
|
+
const l1y = next.y * scale + offsetY + n1.y * halfW1;
|
|
387
|
+
const r1x = next.x * scale + offsetX - n1.x * halfW1;
|
|
388
|
+
const r1y = next.y * scale + offsetY - n1.y * halfW1;
|
|
389
|
+
ctx.fillStyle = `rgba(${trailRgb},${alpha})`;
|
|
331
390
|
ctx.beginPath();
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
ctx.strokeStyle = `rgba(${trailRgb},${alpha})`;
|
|
341
|
-
ctx.lineWidth = lineWidth;
|
|
342
|
-
ctx.stroke();
|
|
391
|
+
ctx.moveTo(l0x, l0y);
|
|
392
|
+
ctx.lineTo(l1x, l1y);
|
|
393
|
+
ctx.lineTo(r1x, r1y);
|
|
394
|
+
ctx.lineTo(r0x, r0y);
|
|
395
|
+
ctx.closePath();
|
|
396
|
+
ctx.fill();
|
|
343
397
|
}
|
|
344
398
|
}
|
|
345
399
|
function drawHead() {
|
|
@@ -348,14 +402,6 @@ function createRenderer(options) {
|
|
|
348
402
|
}
|
|
349
403
|
const x = head.x * scale + offsetX;
|
|
350
404
|
const y = head.y * scale + offsetY;
|
|
351
|
-
const gradient = ctx.createRadialGradient(x, y, 0, x, y, opts.glowSize);
|
|
352
|
-
gradient.addColorStop(0, opts.headColor);
|
|
353
|
-
gradient.addColorStop(GLOW_INNER_EDGE, headRgbFalloff);
|
|
354
|
-
gradient.addColorStop(1, "transparent");
|
|
355
|
-
ctx.fillStyle = gradient;
|
|
356
|
-
ctx.beginPath();
|
|
357
|
-
ctx.arc(x, y, opts.glowSize, 0, Math.PI * 2);
|
|
358
|
-
ctx.fill();
|
|
359
405
|
ctx.fillStyle = opts.headColor;
|
|
360
406
|
ctx.beginPath();
|
|
361
407
|
ctx.arc(x, y, opts.headRadius, 0, Math.PI * 2);
|
|
@@ -389,7 +435,7 @@ function createRenderer(options) {
|
|
|
389
435
|
trail = engine.tick(deltaTime);
|
|
390
436
|
trailCount = engine.trailCount;
|
|
391
437
|
head = trailCount > 0 ? trail[trailCount - 1] : null;
|
|
392
|
-
ctx.clearRect(0, 0,
|
|
438
|
+
ctx.clearRect(0, 0, logicalWidth, logicalHeight);
|
|
393
439
|
if (engine.isLiveSkeleton && engine.morphAlpha === null) {
|
|
394
440
|
skeleton = engine.getSarmalSkeleton();
|
|
395
441
|
calculateBoundaries();
|
|
@@ -449,22 +495,19 @@ function createRenderer(options) {
|
|
|
449
495
|
return new Promise((resolve) => {
|
|
450
496
|
morphResolve = resolve;
|
|
451
497
|
});
|
|
452
|
-
}
|
|
498
|
+
},
|
|
453
499
|
};
|
|
454
500
|
}
|
|
455
501
|
|
|
456
502
|
// src/renderer-svg.ts
|
|
457
503
|
var DEFAULT_MORPH_DURATION_MS2 = 300;
|
|
458
|
-
var
|
|
504
|
+
var MAX_TRAIL_SEGMENTS = 200;
|
|
459
505
|
var TRAIL_FADE_CURVE2 = 1.5;
|
|
460
506
|
var TRAIL_MAX_OPACITY2 = 0.88;
|
|
461
507
|
var TRAIL_MIN_WIDTH2 = 0.5;
|
|
462
508
|
var TRAIL_MAX_WIDTH2 = 2.5;
|
|
463
509
|
var DEFAULT_SKELETON_OPACITY2 = 0.15;
|
|
464
|
-
var DEFAULT_GLOW_INNER_STOP = 0.4;
|
|
465
|
-
var DEFAULT_GLOW_FALLOFF_OPACITY = 0.53;
|
|
466
510
|
var FIT_PADDING2 = 0.1;
|
|
467
|
-
var instanceCount = 0;
|
|
468
511
|
function el(tag) {
|
|
469
512
|
return document.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
470
513
|
}
|
|
@@ -475,11 +518,8 @@ function createSVGRenderer(options) {
|
|
|
475
518
|
trailColor: options.trailColor ?? "#ffffff",
|
|
476
519
|
headColor: options.headColor ?? "#ffffff",
|
|
477
520
|
headRadius: options.headRadius ?? 4,
|
|
478
|
-
|
|
479
|
-
ariaLabel: options.ariaLabel ?? "Loading"
|
|
521
|
+
ariaLabel: options.ariaLabel ?? "Loading",
|
|
480
522
|
};
|
|
481
|
-
const uid = ++instanceCount;
|
|
482
|
-
const gradientId = `sarmal-glow-${uid}`;
|
|
483
523
|
const rect = container.getBoundingClientRect();
|
|
484
524
|
const width = rect.width || 200;
|
|
485
525
|
const height = rect.height || 200;
|
|
@@ -492,27 +532,6 @@ function createSVGRenderer(options) {
|
|
|
492
532
|
const titleEl = el("title");
|
|
493
533
|
titleEl.textContent = opts.ariaLabel;
|
|
494
534
|
svg.appendChild(titleEl);
|
|
495
|
-
const defs = el("defs");
|
|
496
|
-
const gradient = el("radialGradient");
|
|
497
|
-
gradient.id = gradientId;
|
|
498
|
-
gradient.setAttribute("cx", "50%");
|
|
499
|
-
gradient.setAttribute("cy", "50%");
|
|
500
|
-
gradient.setAttribute("r", "50%");
|
|
501
|
-
const stop0 = el("stop");
|
|
502
|
-
stop0.setAttribute("offset", "0%");
|
|
503
|
-
stop0.setAttribute("stop-color", opts.headColor);
|
|
504
|
-
stop0.setAttribute("stop-opacity", "1");
|
|
505
|
-
const stopMid = el("stop");
|
|
506
|
-
stopMid.setAttribute("offset", `${DEFAULT_GLOW_INNER_STOP * 100}%`);
|
|
507
|
-
stopMid.setAttribute("stop-color", opts.headColor);
|
|
508
|
-
stopMid.setAttribute("stop-opacity", String(DEFAULT_GLOW_FALLOFF_OPACITY));
|
|
509
|
-
const stop1 = el("stop");
|
|
510
|
-
stop1.setAttribute("offset", "100%");
|
|
511
|
-
stop1.setAttribute("stop-color", opts.headColor);
|
|
512
|
-
stop1.setAttribute("stop-opacity", "0");
|
|
513
|
-
gradient.append(stop0, stopMid, stop1);
|
|
514
|
-
defs.appendChild(gradient);
|
|
515
|
-
svg.appendChild(defs);
|
|
516
535
|
const skeletonPath = el("path");
|
|
517
536
|
skeletonPath.setAttribute("fill", "none");
|
|
518
537
|
skeletonPath.setAttribute("stroke", opts.skeletonColor);
|
|
@@ -534,19 +553,12 @@ function createSVGRenderer(options) {
|
|
|
534
553
|
let morphPathABuilt = "";
|
|
535
554
|
let morphPathBBuilt = "";
|
|
536
555
|
const trailPaths = [];
|
|
537
|
-
for (let i = 0; i <
|
|
556
|
+
for (let i = 0; i < MAX_TRAIL_SEGMENTS; i++) {
|
|
538
557
|
const path = el("path");
|
|
539
|
-
path.setAttribute("fill",
|
|
540
|
-
path.setAttribute("stroke", opts.trailColor);
|
|
541
|
-
path.setAttribute("stroke-linecap", "round");
|
|
542
|
-
path.setAttribute("stroke-linejoin", "round");
|
|
558
|
+
path.setAttribute("fill", opts.trailColor);
|
|
543
559
|
svg.appendChild(path);
|
|
544
560
|
trailPaths.push(path);
|
|
545
561
|
}
|
|
546
|
-
const glowCircle = el("circle");
|
|
547
|
-
glowCircle.setAttribute("fill", `url(#${gradientId})`);
|
|
548
|
-
glowCircle.setAttribute("r", String(opts.glowSize));
|
|
549
|
-
svg.appendChild(glowCircle);
|
|
550
562
|
const headCircle = el("circle");
|
|
551
563
|
headCircle.setAttribute("fill", opts.headColor);
|
|
552
564
|
headCircle.setAttribute("r", String(opts.headRadius));
|
|
@@ -560,7 +572,10 @@ function createSVGRenderer(options) {
|
|
|
560
572
|
return;
|
|
561
573
|
}
|
|
562
574
|
const first = skeleton2[0];
|
|
563
|
-
let minX = first.x,
|
|
575
|
+
let minX = first.x,
|
|
576
|
+
maxX = first.x,
|
|
577
|
+
minY = first.y,
|
|
578
|
+
maxY = first.y;
|
|
564
579
|
for (const p of skeleton2) {
|
|
565
580
|
if (p.x < minX) {
|
|
566
581
|
minX = p.x;
|
|
@@ -613,24 +628,32 @@ function createSVGRenderer(options) {
|
|
|
613
628
|
}
|
|
614
629
|
return;
|
|
615
630
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
const
|
|
619
|
-
const end = Math.min(start + batchSize, trailCount - 1);
|
|
620
|
-
if (start >= trailCount - 1) {
|
|
621
|
-
trailPaths[b].setAttribute("d", "");
|
|
622
|
-
continue;
|
|
623
|
-
}
|
|
624
|
-
const progress = (start + end) / 2 / (trailCount - 1);
|
|
631
|
+
for (let i = 0; i < trailCount - 1; i++) {
|
|
632
|
+
const progress = i / (trailCount - 1);
|
|
633
|
+
const nextProgress = (i + 1) / (trailCount - 1);
|
|
625
634
|
const opacity = Math.pow(progress, TRAIL_FADE_CURVE2) * TRAIL_MAX_OPACITY2;
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
635
|
+
const width2 = TRAIL_MIN_WIDTH2 + progress * (TRAIL_MAX_WIDTH2 - TRAIL_MIN_WIDTH2);
|
|
636
|
+
const nextWidth = TRAIL_MIN_WIDTH2 + nextProgress * (TRAIL_MAX_WIDTH2 - TRAIL_MIN_WIDTH2);
|
|
637
|
+
const curr = trail[i];
|
|
638
|
+
const next = trail[i + 1];
|
|
639
|
+
const n0 = computeNormal(trail, i);
|
|
640
|
+
const n1 = computeNormal(trail, i + 1);
|
|
641
|
+
const halfW0 = width2 / 2;
|
|
642
|
+
const halfW1 = nextWidth / 2;
|
|
643
|
+
const l0x = px(curr) + n0.x * halfW0;
|
|
644
|
+
const l0y = py(curr) + n0.y * halfW0;
|
|
645
|
+
const r0x = px(curr) - n0.x * halfW0;
|
|
646
|
+
const r0y = py(curr) - n0.y * halfW0;
|
|
647
|
+
const l1x = px(next) + n1.x * halfW1;
|
|
648
|
+
const l1y = py(next) + n1.y * halfW1;
|
|
649
|
+
const r1x = px(next) - n1.x * halfW1;
|
|
650
|
+
const r1y = py(next) - n1.y * halfW1;
|
|
651
|
+
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`;
|
|
652
|
+
trailPaths[i].setAttribute("d", d);
|
|
653
|
+
trailPaths[i].setAttribute("fill-opacity", opacity.toFixed(3));
|
|
654
|
+
}
|
|
655
|
+
for (let i = trailCount - 1; i < trailPaths.length; i++) {
|
|
656
|
+
trailPaths[i].setAttribute("d", "");
|
|
634
657
|
}
|
|
635
658
|
}
|
|
636
659
|
function updateHead(trail, trailCount) {
|
|
@@ -640,14 +663,13 @@ function createSVGRenderer(options) {
|
|
|
640
663
|
const head = trail[trailCount - 1];
|
|
641
664
|
const x = px(head);
|
|
642
665
|
const y = py(head);
|
|
643
|
-
glowCircle.setAttribute("cx", x);
|
|
644
|
-
glowCircle.setAttribute("cy", y);
|
|
645
666
|
headCircle.setAttribute("cx", x);
|
|
646
667
|
headCircle.setAttribute("cy", y);
|
|
647
668
|
}
|
|
648
669
|
let animationId = null;
|
|
649
670
|
let lastTime = 0;
|
|
650
|
-
const prefersReducedMotion =
|
|
671
|
+
const prefersReducedMotion =
|
|
672
|
+
typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
651
673
|
let morphResolve = null;
|
|
652
674
|
let morphDurationMs = DEFAULT_MORPH_DURATION_MS2;
|
|
653
675
|
let morphTarget = null;
|
|
@@ -657,7 +679,7 @@ function createSVGRenderer(options) {
|
|
|
657
679
|
const samples = Math.max(50, Math.round(period * 20));
|
|
658
680
|
const points = [];
|
|
659
681
|
for (let i = 0; i <= samples; i++) {
|
|
660
|
-
const t = i / samples * period;
|
|
682
|
+
const t = (i / samples) * period;
|
|
661
683
|
const p = target.fn(t, 0, {});
|
|
662
684
|
points.push(p);
|
|
663
685
|
}
|
|
@@ -685,13 +707,16 @@ function createSVGRenderer(options) {
|
|
|
685
707
|
skeletonPathA.setAttribute("visibility", "visible");
|
|
686
708
|
skeletonPathA.setAttribute(
|
|
687
709
|
"stroke-opacity",
|
|
688
|
-
String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY2)
|
|
710
|
+
String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY2),
|
|
689
711
|
);
|
|
690
712
|
}
|
|
691
713
|
if (morphPathBBuilt) {
|
|
692
714
|
skeletonPathB.setAttribute("d", morphPathBBuilt);
|
|
693
715
|
skeletonPathB.setAttribute("visibility", "visible");
|
|
694
|
-
skeletonPathB.setAttribute(
|
|
716
|
+
skeletonPathB.setAttribute(
|
|
717
|
+
"stroke-opacity",
|
|
718
|
+
String(morphAlpha * DEFAULT_SKELETON_OPACITY2),
|
|
719
|
+
);
|
|
695
720
|
}
|
|
696
721
|
if (morphAlpha >= 1) {
|
|
697
722
|
engine.completeMorph();
|
|
@@ -783,7 +808,7 @@ function createSVGRenderer(options) {
|
|
|
783
808
|
return new Promise((resolve) => {
|
|
784
809
|
morphResolve = resolve;
|
|
785
810
|
});
|
|
786
|
-
}
|
|
811
|
+
},
|
|
787
812
|
};
|
|
788
813
|
}
|
|
789
814
|
function createSarmalSVG(container, curveDef, options) {
|
|
@@ -795,26 +820,29 @@ function createSarmalSVG(container, curveDef, options) {
|
|
|
795
820
|
// src/curves.ts
|
|
796
821
|
var TWO_PI2 = Math.PI * 2;
|
|
797
822
|
function artemis2(t, _time, _params) {
|
|
798
|
-
const a = 0.35,
|
|
799
|
-
|
|
823
|
+
const a = 0.35,
|
|
824
|
+
b = 0.15,
|
|
825
|
+
ox = 0.175;
|
|
826
|
+
const s = Math.sin(t),
|
|
827
|
+
c = Math.cos(t);
|
|
800
828
|
const denom = 1 + s * s;
|
|
801
829
|
return {
|
|
802
|
-
x: c * (1 + a * c) / denom - ox,
|
|
803
|
-
y: s * c * (1 + b * c) / denom
|
|
830
|
+
x: (c * (1 + a * c)) / denom - ox,
|
|
831
|
+
y: (s * c * (1 + b * c)) / denom,
|
|
804
832
|
};
|
|
805
833
|
}
|
|
806
834
|
function epitrochoid7(t, _time, _params) {
|
|
807
835
|
const d = 1 + 0.55 * Math.sin(t * 0.5);
|
|
808
836
|
return {
|
|
809
837
|
x: 7 * Math.cos(t) - d * Math.cos(7 * t),
|
|
810
|
-
y: 7 * Math.sin(t) - d * Math.sin(7 * t)
|
|
838
|
+
y: 7 * Math.sin(t) - d * Math.sin(7 * t),
|
|
811
839
|
};
|
|
812
840
|
}
|
|
813
841
|
function epitrochoid7Skeleton(t) {
|
|
814
842
|
const d = 1.275;
|
|
815
843
|
return {
|
|
816
844
|
x: 7 * Math.cos(t) - d * Math.cos(7 * t),
|
|
817
|
-
y: 7 * Math.sin(t) - d * Math.sin(7 * t)
|
|
845
|
+
y: 7 * Math.sin(t) - d * Math.sin(7 * t),
|
|
818
846
|
};
|
|
819
847
|
}
|
|
820
848
|
function astroid(t, _time, _params) {
|
|
@@ -822,55 +850,56 @@ function astroid(t, _time, _params) {
|
|
|
822
850
|
const s = Math.sin(t);
|
|
823
851
|
return {
|
|
824
852
|
x: c * c * c,
|
|
825
|
-
y: s * s * s
|
|
853
|
+
y: s * s * s,
|
|
826
854
|
};
|
|
827
855
|
}
|
|
828
856
|
function deltoid(t, _time, _params) {
|
|
829
857
|
return {
|
|
830
858
|
x: 2 * Math.cos(t) + Math.cos(2 * t),
|
|
831
|
-
y: 2 * Math.sin(t) - Math.sin(2 * t)
|
|
859
|
+
y: 2 * Math.sin(t) - Math.sin(2 * t),
|
|
832
860
|
};
|
|
833
861
|
}
|
|
834
862
|
function rose5(t, _time, _params) {
|
|
835
863
|
const r = Math.cos(5 * t);
|
|
836
864
|
return {
|
|
837
865
|
x: r * Math.cos(t),
|
|
838
|
-
y: r * Math.sin(t)
|
|
866
|
+
y: r * Math.sin(t),
|
|
839
867
|
};
|
|
840
868
|
}
|
|
841
869
|
function rose3(t, _time, _params) {
|
|
842
870
|
const r = Math.cos(3 * t);
|
|
843
871
|
return {
|
|
844
872
|
x: r * Math.cos(t),
|
|
845
|
-
y: r * Math.sin(t)
|
|
873
|
+
y: r * Math.sin(t),
|
|
846
874
|
};
|
|
847
875
|
}
|
|
848
876
|
function lissajous32(t, time, _params) {
|
|
849
877
|
const phi = time * 0.45;
|
|
850
878
|
return {
|
|
851
879
|
x: Math.sin(3 * t + phi),
|
|
852
|
-
y: Math.sin(2 * t)
|
|
880
|
+
y: Math.sin(2 * t),
|
|
853
881
|
};
|
|
854
882
|
}
|
|
855
883
|
function lissajous43(t, time, _params) {
|
|
856
884
|
const phi = time * 0.38;
|
|
857
885
|
return {
|
|
858
886
|
x: Math.sin(4 * t + phi),
|
|
859
|
-
y: Math.sin(3 * t)
|
|
887
|
+
y: Math.sin(3 * t),
|
|
860
888
|
};
|
|
861
889
|
}
|
|
862
890
|
function epicycloid3(t, _time, _params) {
|
|
863
891
|
return {
|
|
864
892
|
x: 4 * Math.cos(t) - Math.cos(4 * t),
|
|
865
|
-
y: 4 * Math.sin(t) - Math.sin(4 * t)
|
|
893
|
+
y: 4 * Math.sin(t) - Math.sin(4 * t),
|
|
866
894
|
};
|
|
867
895
|
}
|
|
868
896
|
function lame(t, time, _params) {
|
|
869
897
|
const p = 1.75 + 1.25 * Math.sin(time * 0.48);
|
|
870
|
-
const c = Math.cos(t),
|
|
898
|
+
const c = Math.cos(t),
|
|
899
|
+
s = Math.sin(t);
|
|
871
900
|
return {
|
|
872
901
|
x: Math.sign(c) * Math.pow(Math.abs(c), p),
|
|
873
|
-
y: Math.sign(s) * Math.pow(Math.abs(s), p)
|
|
902
|
+
y: Math.sign(s) * Math.pow(Math.abs(s), p),
|
|
874
903
|
};
|
|
875
904
|
}
|
|
876
905
|
var curves = {
|
|
@@ -878,66 +907,66 @@ var curves = {
|
|
|
878
907
|
name: "Artemis II",
|
|
879
908
|
fn: artemis2,
|
|
880
909
|
period: TWO_PI2,
|
|
881
|
-
speed: 0.7
|
|
910
|
+
speed: 0.7,
|
|
882
911
|
},
|
|
883
912
|
epitrochoid7: {
|
|
884
913
|
name: "Epitrochoid",
|
|
885
914
|
fn: epitrochoid7,
|
|
886
915
|
period: TWO_PI2,
|
|
887
916
|
speed: 1.4,
|
|
888
|
-
skeletonFn: epitrochoid7Skeleton
|
|
917
|
+
skeletonFn: epitrochoid7Skeleton,
|
|
889
918
|
},
|
|
890
919
|
astroid: {
|
|
891
920
|
name: "Astroid",
|
|
892
921
|
fn: astroid,
|
|
893
922
|
period: TWO_PI2,
|
|
894
|
-
speed: 1.1
|
|
923
|
+
speed: 1.1,
|
|
895
924
|
},
|
|
896
925
|
deltoid: {
|
|
897
926
|
name: "Deltoid",
|
|
898
927
|
fn: deltoid,
|
|
899
928
|
period: TWO_PI2,
|
|
900
|
-
speed: 0.9
|
|
929
|
+
speed: 0.9,
|
|
901
930
|
},
|
|
902
931
|
rose5: {
|
|
903
932
|
name: "Rose (n=5)",
|
|
904
933
|
fn: rose5,
|
|
905
934
|
period: TWO_PI2,
|
|
906
|
-
speed: 1
|
|
935
|
+
speed: 1,
|
|
907
936
|
},
|
|
908
937
|
rose3: {
|
|
909
938
|
name: "Rose (n=3)",
|
|
910
939
|
fn: rose3,
|
|
911
940
|
period: TWO_PI2,
|
|
912
|
-
speed: 1.15
|
|
941
|
+
speed: 1.15,
|
|
913
942
|
},
|
|
914
943
|
lissajous32: {
|
|
915
944
|
name: "Lissajous 3:2",
|
|
916
945
|
fn: lissajous32,
|
|
917
946
|
period: TWO_PI2,
|
|
918
947
|
speed: 2,
|
|
919
|
-
skeleton: "live"
|
|
948
|
+
skeleton: "live",
|
|
920
949
|
},
|
|
921
950
|
lissajous43: {
|
|
922
951
|
name: "Lissajous 4:3",
|
|
923
952
|
fn: lissajous43,
|
|
924
953
|
period: TWO_PI2,
|
|
925
954
|
speed: 1.8,
|
|
926
|
-
skeleton: "live"
|
|
955
|
+
skeleton: "live",
|
|
927
956
|
},
|
|
928
957
|
epicycloid3: {
|
|
929
958
|
name: "Epicycloid (n=3)",
|
|
930
959
|
fn: epicycloid3,
|
|
931
960
|
period: TWO_PI2,
|
|
932
|
-
speed: 0.75
|
|
961
|
+
speed: 0.75,
|
|
933
962
|
},
|
|
934
963
|
lame: {
|
|
935
964
|
name: "Lam\xE9 Curve",
|
|
936
965
|
fn: lame,
|
|
937
966
|
period: TWO_PI2,
|
|
938
967
|
speed: 1,
|
|
939
|
-
skeleton: "live"
|
|
940
|
-
}
|
|
968
|
+
skeleton: "live",
|
|
969
|
+
},
|
|
941
970
|
};
|
|
942
971
|
|
|
943
972
|
// src/index.ts
|
|
@@ -954,4 +983,4 @@ exports.createSarmal = createSarmal;
|
|
|
954
983
|
exports.createSarmalSVG = createSarmalSVG;
|
|
955
984
|
exports.curves = curves;
|
|
956
985
|
//# sourceMappingURL=index.cjs.map
|
|
957
|
-
//# sourceMappingURL=index.cjs.map
|
|
986
|
+
//# sourceMappingURL=index.cjs.map
|