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