@sarmal/core 0.7.1 → 0.9.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 +130 -41
- package/dist/auto-init.cjs.map +1 -1
- package/dist/auto-init.js +130 -41
- package/dist/auto-init.js.map +1 -1
- package/dist/index.cjs +170 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -6
- package/dist/index.d.ts +18 -6
- package/dist/index.js +170 -102
- package/dist/index.js.map +1 -1
- package/package.json +12 -2
package/dist/index.cjs
CHANGED
|
@@ -188,21 +188,90 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
188
188
|
// src/renderer.ts
|
|
189
189
|
var DEFAULT_MORPH_DURATION_MS = 300;
|
|
190
190
|
var DEFAULT_HEAD_RADIUS = 4;
|
|
191
|
-
var DEFAULT_GLOW_SIZE = 20;
|
|
192
191
|
var DEFAULT_SKELETON_COLOR = "#ffffff";
|
|
193
192
|
var DEFAULT_SKELETON_OPACITY = 0.15;
|
|
194
193
|
var FIT_PADDING = 0.1;
|
|
195
|
-
var TRAIL_BATCH_SIZE = 20;
|
|
196
194
|
var TRAIL_FADE_CURVE = 1.5;
|
|
197
195
|
var TRAIL_MAX_OPACITY = 0.88;
|
|
198
196
|
var TRAIL_MIN_WIDTH = 0.5;
|
|
199
197
|
var TRAIL_MAX_WIDTH = 2.5;
|
|
200
|
-
var
|
|
201
|
-
|
|
198
|
+
var GRADIENT = {
|
|
199
|
+
bard: ["#a855f7", "#3b82f6", "#14b8a6", "#ec4899"],
|
|
200
|
+
sunset: ["#f97316", "#dc2626", "#9333ea", "#f472b6"],
|
|
201
|
+
ocean: ["#1e3a8a", "#06b6d4", "#22d3ee", "#e0f2fe"],
|
|
202
|
+
ice: ["#1e3a8a", "#67e8f9"],
|
|
203
|
+
fire: ["#7f1d1d", "#fbbf24"],
|
|
204
|
+
forest: ["#14532d", "#86efac"]
|
|
205
|
+
};
|
|
206
|
+
var PRESETS = {
|
|
207
|
+
bard: GRADIENT.bard,
|
|
208
|
+
sunset: GRADIENT.sunset,
|
|
209
|
+
ocean: GRADIENT.ocean,
|
|
210
|
+
ice: GRADIENT.ice,
|
|
211
|
+
fire: GRADIENT.fire,
|
|
212
|
+
forest: GRADIENT.forest
|
|
213
|
+
};
|
|
214
|
+
function hexToRgb(hex) {
|
|
215
|
+
const n = parseInt(hex.slice(1), 16);
|
|
216
|
+
return { r: n >> 16, g: n >> 8 & 255, b: n & 255 };
|
|
217
|
+
}
|
|
218
|
+
var lerpRgb = (a, b, t) => ({
|
|
219
|
+
r: Math.round(a.r + (b.r - a.r) * t),
|
|
220
|
+
g: Math.round(a.g + (b.g - a.g) * t),
|
|
221
|
+
b: Math.round(a.b + (b.b - a.b) * t)
|
|
222
|
+
});
|
|
223
|
+
function getPaletteColor(palette, position, timeOffset = 0) {
|
|
224
|
+
if (palette.length === 0) return { r: 255, g: 255, b: 255 };
|
|
225
|
+
if (palette.length === 1) return hexToRgb(palette[0]);
|
|
226
|
+
const cyclePos = (position + timeOffset) % 1;
|
|
227
|
+
const scaled = cyclePos * palette.length;
|
|
228
|
+
const idx = Math.floor(scaled);
|
|
229
|
+
const t = scaled - idx;
|
|
230
|
+
const c1 = hexToRgb(palette[idx % palette.length]);
|
|
231
|
+
const c2 = hexToRgb(palette[(idx + 1) % palette.length]);
|
|
232
|
+
return lerpRgb(c1, c2, t);
|
|
233
|
+
}
|
|
234
|
+
function resolvePalette(palette, trailStyle) {
|
|
235
|
+
if (Array.isArray(palette)) return palette;
|
|
236
|
+
if (palette && palette in PRESETS) return PRESETS[palette];
|
|
237
|
+
return trailStyle === "gradient-animated" ? GRADIENT.bard : GRADIENT.ice;
|
|
238
|
+
}
|
|
202
239
|
function hexToRgbComponents(hex) {
|
|
203
240
|
const n = parseInt(hex.slice(1), 16);
|
|
204
241
|
return `${n >> 16},${n >> 8 & 255},${n & 255}`;
|
|
205
242
|
}
|
|
243
|
+
function computeTangent(trail, i) {
|
|
244
|
+
const count = trail.length;
|
|
245
|
+
if (count < 2) {
|
|
246
|
+
return { x: 1, y: 0 };
|
|
247
|
+
}
|
|
248
|
+
if (i === 0) {
|
|
249
|
+
const dx2 = trail[1].x - trail[0].x;
|
|
250
|
+
const dy2 = trail[1].y - trail[0].y;
|
|
251
|
+
const len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2) || 1;
|
|
252
|
+
return { x: dx2 / len2, y: dy2 / len2 };
|
|
253
|
+
}
|
|
254
|
+
if (i === count - 1) {
|
|
255
|
+
const dx2 = trail[count - 1].x - trail[count - 2].x;
|
|
256
|
+
const dy2 = trail[count - 1].y - trail[count - 2].y;
|
|
257
|
+
const len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2) || 1;
|
|
258
|
+
return { x: dx2 / len2, y: dy2 / len2 };
|
|
259
|
+
}
|
|
260
|
+
const dx = trail[i + 1].x - trail[i - 1].x;
|
|
261
|
+
const dy = trail[i + 1].y - trail[i - 1].y;
|
|
262
|
+
const len = Math.sqrt(dx * dx + dy * dy) || 1;
|
|
263
|
+
return { x: dx / len, y: dy / len };
|
|
264
|
+
}
|
|
265
|
+
function computeNormal(trail, i) {
|
|
266
|
+
const tangent = computeTangent(trail, i);
|
|
267
|
+
return { x: -tangent.y, y: tangent.x };
|
|
268
|
+
}
|
|
269
|
+
function applyDprSizing(target, logicalWidth, logicalHeight, dpr) {
|
|
270
|
+
target.style.width = `${logicalWidth}px`;
|
|
271
|
+
target.style.height = `${logicalHeight}px`;
|
|
272
|
+
target.width = logicalWidth * dpr;
|
|
273
|
+
target.height = logicalHeight * dpr;
|
|
274
|
+
}
|
|
206
275
|
function createRenderer(options) {
|
|
207
276
|
const canvas = options.canvas;
|
|
208
277
|
if (!canvas.getContext("2d")) {
|
|
@@ -214,11 +283,22 @@ function createRenderer(options) {
|
|
|
214
283
|
skeletonColor: options.skeletonColor ?? DEFAULT_SKELETON_COLOR,
|
|
215
284
|
trailColor: options.trailColor ?? "#ffffff",
|
|
216
285
|
headColor: options.headColor ?? "#ffffff",
|
|
217
|
-
headRadius: options.headRadius ?? DEFAULT_HEAD_RADIUS
|
|
218
|
-
glowSize: options.glowSize ?? DEFAULT_GLOW_SIZE
|
|
286
|
+
headRadius: options.headRadius ?? DEFAULT_HEAD_RADIUS
|
|
219
287
|
};
|
|
288
|
+
const trailStyle = options.trailStyle ?? "default";
|
|
289
|
+
const palette = resolvePalette(options.palette, trailStyle);
|
|
220
290
|
const trailRgb = hexToRgbComponents(opts.trailColor);
|
|
221
|
-
const
|
|
291
|
+
const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
|
292
|
+
function setupCanvas() {
|
|
293
|
+
const rect = canvas.getBoundingClientRect();
|
|
294
|
+
const lw = rect.width || 200;
|
|
295
|
+
const lh = rect.height || 200;
|
|
296
|
+
applyDprSizing(canvas, lw, lh, dpr);
|
|
297
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
298
|
+
}
|
|
299
|
+
setupCanvas();
|
|
300
|
+
let logicalWidth = canvas.width / dpr;
|
|
301
|
+
let logicalHeight = canvas.height / dpr;
|
|
222
302
|
let skeleton = [];
|
|
223
303
|
let skeletonCanvas = null;
|
|
224
304
|
let trail = [];
|
|
@@ -232,6 +312,7 @@ function createRenderer(options) {
|
|
|
232
312
|
let morphResolve = null;
|
|
233
313
|
let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
|
|
234
314
|
let morphAlpha = 0;
|
|
315
|
+
let gradientAnimTime = 0;
|
|
235
316
|
function computeBoundaries(pts) {
|
|
236
317
|
if (pts.length === 0) return null;
|
|
237
318
|
const first = pts[0];
|
|
@@ -244,17 +325,15 @@ function createRenderer(options) {
|
|
|
244
325
|
}
|
|
245
326
|
const width = maxX - minX;
|
|
246
327
|
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));
|
|
328
|
+
const scaleX = logicalWidth / (width * (1 + FIT_PADDING * 2));
|
|
329
|
+
const scaleY = logicalHeight / (height * (1 + FIT_PADDING * 2));
|
|
251
330
|
const s = Math.min(scaleX, scaleY);
|
|
252
331
|
const boundsWidth = width * s;
|
|
253
332
|
const boundsHeight = height * s;
|
|
254
333
|
return {
|
|
255
334
|
scale: s,
|
|
256
|
-
offsetX: (
|
|
257
|
-
offsetY: (
|
|
335
|
+
offsetX: (logicalWidth - boundsWidth) / 2 - minX * s,
|
|
336
|
+
offsetY: (logicalHeight - boundsHeight) / 2 - minY * s
|
|
258
337
|
};
|
|
259
338
|
}
|
|
260
339
|
function calculateBoundaries() {
|
|
@@ -269,6 +348,7 @@ function createRenderer(options) {
|
|
|
269
348
|
if (skeleton.length < 2) return;
|
|
270
349
|
skeletonCanvas = new OffscreenCanvas(canvas.width, canvas.height);
|
|
271
350
|
const skeletonCtx = skeletonCanvas.getContext("2d");
|
|
351
|
+
skeletonCtx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
272
352
|
skeletonCtx.strokeStyle = `rgba(${hexToRgbComponents(opts.skeletonColor)},${DEFAULT_SKELETON_OPACITY})`;
|
|
273
353
|
skeletonCtx.lineWidth = 1.5;
|
|
274
354
|
skeletonCtx.beginPath();
|
|
@@ -314,32 +394,47 @@ function createRenderer(options) {
|
|
|
314
394
|
}
|
|
315
395
|
ctx.stroke();
|
|
316
396
|
} else if (skeletonCanvas) {
|
|
317
|
-
ctx.drawImage(skeletonCanvas, 0, 0);
|
|
397
|
+
ctx.drawImage(skeletonCanvas, 0, 0, logicalWidth, logicalHeight);
|
|
318
398
|
}
|
|
319
399
|
}
|
|
320
400
|
function drawTrail() {
|
|
321
401
|
if (trailCount < 2) {
|
|
322
402
|
return;
|
|
323
403
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
const bEnd = Math.min(batchIndex + TRAIL_BATCH_SIZE, trailCount - 1);
|
|
328
|
-
const progress = (batchIndex + bEnd) / 2 / (trailCount - 1);
|
|
404
|
+
for (let i = 0; i < trailCount - 1; i++) {
|
|
405
|
+
const progress = i / (trailCount - 1);
|
|
406
|
+
const nextProgress = (i + 1) / (trailCount - 1);
|
|
329
407
|
const alpha = Math.pow(progress, TRAIL_FADE_CURVE) * TRAIL_MAX_OPACITY;
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
408
|
+
const width = TRAIL_MIN_WIDTH + progress * (TRAIL_MAX_WIDTH - TRAIL_MIN_WIDTH);
|
|
409
|
+
const nextWidth = TRAIL_MIN_WIDTH + nextProgress * (TRAIL_MAX_WIDTH - TRAIL_MIN_WIDTH);
|
|
410
|
+
const curr = trail[i];
|
|
411
|
+
const next = trail[i + 1];
|
|
412
|
+
const n0 = computeNormal(trail, i);
|
|
413
|
+
const n1 = computeNormal(trail, i + 1);
|
|
414
|
+
const halfW0 = width / 2;
|
|
415
|
+
const halfW1 = nextWidth / 2;
|
|
416
|
+
const l0x = curr.x * scale + offsetX + n0.x * halfW0;
|
|
417
|
+
const l0y = curr.y * scale + offsetY + n0.y * halfW0;
|
|
418
|
+
const r0x = curr.x * scale + offsetX - n0.x * halfW0;
|
|
419
|
+
const r0y = curr.y * scale + offsetY - n0.y * halfW0;
|
|
420
|
+
const l1x = next.x * scale + offsetX + n1.x * halfW1;
|
|
421
|
+
const l1y = next.y * scale + offsetY + n1.y * halfW1;
|
|
422
|
+
const r1x = next.x * scale + offsetX - n1.x * halfW1;
|
|
423
|
+
const r1y = next.y * scale + offsetY - n1.y * halfW1;
|
|
424
|
+
if (trailStyle === "default") {
|
|
425
|
+
ctx.fillStyle = `rgba(${trailRgb},${alpha})`;
|
|
426
|
+
} else {
|
|
427
|
+
const timeOffset = trailStyle === "gradient-animated" ? gradientAnimTime * 5e-4 : 0;
|
|
428
|
+
const color = getPaletteColor(palette, progress, timeOffset);
|
|
429
|
+
ctx.fillStyle = `rgba(${color.r},${color.g},${color.b},${alpha})`;
|
|
339
430
|
}
|
|
340
|
-
ctx.
|
|
341
|
-
ctx.
|
|
342
|
-
ctx.
|
|
431
|
+
ctx.beginPath();
|
|
432
|
+
ctx.moveTo(l0x, l0y);
|
|
433
|
+
ctx.lineTo(l1x, l1y);
|
|
434
|
+
ctx.lineTo(r1x, r1y);
|
|
435
|
+
ctx.lineTo(r0x, r0y);
|
|
436
|
+
ctx.closePath();
|
|
437
|
+
ctx.fill();
|
|
343
438
|
}
|
|
344
439
|
}
|
|
345
440
|
function drawHead() {
|
|
@@ -348,14 +443,6 @@ function createRenderer(options) {
|
|
|
348
443
|
}
|
|
349
444
|
const x = head.x * scale + offsetX;
|
|
350
445
|
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
446
|
ctx.fillStyle = opts.headColor;
|
|
360
447
|
ctx.beginPath();
|
|
361
448
|
ctx.arc(x, y, opts.headRadius, 0, Math.PI * 2);
|
|
@@ -365,6 +452,9 @@ function createRenderer(options) {
|
|
|
365
452
|
const now = performance.now();
|
|
366
453
|
const deltaTime = Math.min((now - lastTime) / 1e3, 1 / 30);
|
|
367
454
|
lastTime = now;
|
|
455
|
+
if (trailStyle === "gradient-animated") {
|
|
456
|
+
gradientAnimTime += deltaTime * 1e3;
|
|
457
|
+
}
|
|
368
458
|
if (engine.morphAlpha !== null) {
|
|
369
459
|
morphAlpha = Math.min(1, morphAlpha + deltaTime / (morphDurationMs / 1e3));
|
|
370
460
|
engine.setMorphAlpha(morphAlpha);
|
|
@@ -389,7 +479,7 @@ function createRenderer(options) {
|
|
|
389
479
|
trail = engine.tick(deltaTime);
|
|
390
480
|
trailCount = engine.trailCount;
|
|
391
481
|
head = trailCount > 0 ? trail[trailCount - 1] : null;
|
|
392
|
-
ctx.clearRect(0, 0,
|
|
482
|
+
ctx.clearRect(0, 0, logicalWidth, logicalHeight);
|
|
393
483
|
if (engine.isLiveSkeleton && engine.morphAlpha === null) {
|
|
394
484
|
skeleton = engine.getSarmalSkeleton();
|
|
395
485
|
calculateBoundaries();
|
|
@@ -455,16 +545,13 @@ function createRenderer(options) {
|
|
|
455
545
|
|
|
456
546
|
// src/renderer-svg.ts
|
|
457
547
|
var DEFAULT_MORPH_DURATION_MS2 = 300;
|
|
458
|
-
var
|
|
548
|
+
var MAX_TRAIL_SEGMENTS = 200;
|
|
459
549
|
var TRAIL_FADE_CURVE2 = 1.5;
|
|
460
550
|
var TRAIL_MAX_OPACITY2 = 0.88;
|
|
461
551
|
var TRAIL_MIN_WIDTH2 = 0.5;
|
|
462
552
|
var TRAIL_MAX_WIDTH2 = 2.5;
|
|
463
553
|
var DEFAULT_SKELETON_OPACITY2 = 0.15;
|
|
464
|
-
var DEFAULT_GLOW_INNER_STOP = 0.4;
|
|
465
|
-
var DEFAULT_GLOW_FALLOFF_OPACITY = 0.53;
|
|
466
554
|
var FIT_PADDING2 = 0.1;
|
|
467
|
-
var instanceCount = 0;
|
|
468
555
|
function el(tag) {
|
|
469
556
|
return document.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
470
557
|
}
|
|
@@ -475,11 +562,8 @@ function createSVGRenderer(options) {
|
|
|
475
562
|
trailColor: options.trailColor ?? "#ffffff",
|
|
476
563
|
headColor: options.headColor ?? "#ffffff",
|
|
477
564
|
headRadius: options.headRadius ?? 4,
|
|
478
|
-
glowSize: options.glowSize ?? 20,
|
|
479
565
|
ariaLabel: options.ariaLabel ?? "Loading"
|
|
480
566
|
};
|
|
481
|
-
const uid = ++instanceCount;
|
|
482
|
-
const gradientId = `sarmal-glow-${uid}`;
|
|
483
567
|
const rect = container.getBoundingClientRect();
|
|
484
568
|
const width = rect.width || 200;
|
|
485
569
|
const height = rect.height || 200;
|
|
@@ -492,27 +576,6 @@ function createSVGRenderer(options) {
|
|
|
492
576
|
const titleEl = el("title");
|
|
493
577
|
titleEl.textContent = opts.ariaLabel;
|
|
494
578
|
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
579
|
const skeletonPath = el("path");
|
|
517
580
|
skeletonPath.setAttribute("fill", "none");
|
|
518
581
|
skeletonPath.setAttribute("stroke", opts.skeletonColor);
|
|
@@ -534,19 +597,12 @@ function createSVGRenderer(options) {
|
|
|
534
597
|
let morphPathABuilt = "";
|
|
535
598
|
let morphPathBBuilt = "";
|
|
536
599
|
const trailPaths = [];
|
|
537
|
-
for (let i = 0; i <
|
|
600
|
+
for (let i = 0; i < MAX_TRAIL_SEGMENTS; i++) {
|
|
538
601
|
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");
|
|
602
|
+
path.setAttribute("fill", opts.trailColor);
|
|
543
603
|
svg.appendChild(path);
|
|
544
604
|
trailPaths.push(path);
|
|
545
605
|
}
|
|
546
|
-
const glowCircle = el("circle");
|
|
547
|
-
glowCircle.setAttribute("fill", `url(#${gradientId})`);
|
|
548
|
-
glowCircle.setAttribute("r", String(opts.glowSize));
|
|
549
|
-
svg.appendChild(glowCircle);
|
|
550
606
|
const headCircle = el("circle");
|
|
551
607
|
headCircle.setAttribute("fill", opts.headColor);
|
|
552
608
|
headCircle.setAttribute("r", String(opts.headRadius));
|
|
@@ -584,19 +640,25 @@ function createSVGRenderer(options) {
|
|
|
584
640
|
offsetY = (height - h * scale) / 2 - minY * scale;
|
|
585
641
|
}
|
|
586
642
|
function px(p) {
|
|
587
|
-
return
|
|
643
|
+
return p.x * scale + offsetX;
|
|
588
644
|
}
|
|
589
645
|
function py(p) {
|
|
590
|
-
return
|
|
646
|
+
return p.y * scale + offsetY;
|
|
647
|
+
}
|
|
648
|
+
function pxStr(p) {
|
|
649
|
+
return px(p).toFixed(2);
|
|
650
|
+
}
|
|
651
|
+
function pyStr(p) {
|
|
652
|
+
return py(p).toFixed(2);
|
|
591
653
|
}
|
|
592
654
|
function updateSkeleton(skeleton2) {
|
|
593
655
|
if (skeleton2.length < 2) {
|
|
594
656
|
skeletonPath.setAttribute("d", "");
|
|
595
657
|
return;
|
|
596
658
|
}
|
|
597
|
-
let d = `M${
|
|
659
|
+
let d = `M${pxStr(skeleton2[0])} ${pyStr(skeleton2[0])}`;
|
|
598
660
|
for (let i = 1; i < skeleton2.length; i++) {
|
|
599
|
-
d += ` L${
|
|
661
|
+
d += ` L${pxStr(skeleton2[i])} ${pyStr(skeleton2[i])}`;
|
|
600
662
|
}
|
|
601
663
|
d += " Z";
|
|
602
664
|
skeletonPath.setAttribute("d", d);
|
|
@@ -613,24 +675,32 @@ function createSVGRenderer(options) {
|
|
|
613
675
|
}
|
|
614
676
|
return;
|
|
615
677
|
}
|
|
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);
|
|
678
|
+
for (let i = 0; i < trailCount - 1; i++) {
|
|
679
|
+
const progress = i / (trailCount - 1);
|
|
680
|
+
const nextProgress = (i + 1) / (trailCount - 1);
|
|
625
681
|
const opacity = Math.pow(progress, TRAIL_FADE_CURVE2) * TRAIL_MAX_OPACITY2;
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
682
|
+
const width2 = TRAIL_MIN_WIDTH2 + progress * (TRAIL_MAX_WIDTH2 - TRAIL_MIN_WIDTH2);
|
|
683
|
+
const nextWidth = TRAIL_MIN_WIDTH2 + nextProgress * (TRAIL_MAX_WIDTH2 - TRAIL_MIN_WIDTH2);
|
|
684
|
+
const curr = trail[i];
|
|
685
|
+
const next = trail[i + 1];
|
|
686
|
+
const n0 = computeNormal(trail, i);
|
|
687
|
+
const n1 = computeNormal(trail, i + 1);
|
|
688
|
+
const halfW0 = width2 / 2;
|
|
689
|
+
const halfW1 = nextWidth / 2;
|
|
690
|
+
const l0x = px(curr) + n0.x * halfW0;
|
|
691
|
+
const l0y = py(curr) + n0.y * halfW0;
|
|
692
|
+
const r0x = px(curr) - n0.x * halfW0;
|
|
693
|
+
const r0y = py(curr) - n0.y * halfW0;
|
|
694
|
+
const l1x = px(next) + n1.x * halfW1;
|
|
695
|
+
const l1y = py(next) + n1.y * halfW1;
|
|
696
|
+
const r1x = px(next) - n1.x * halfW1;
|
|
697
|
+
const r1y = py(next) - n1.y * halfW1;
|
|
698
|
+
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`;
|
|
699
|
+
trailPaths[i].setAttribute("d", d);
|
|
700
|
+
trailPaths[i].setAttribute("fill-opacity", opacity.toFixed(3));
|
|
701
|
+
}
|
|
702
|
+
for (let i = trailCount - 1; i < trailPaths.length; i++) {
|
|
703
|
+
trailPaths[i].setAttribute("d", "");
|
|
634
704
|
}
|
|
635
705
|
}
|
|
636
706
|
function updateHead(trail, trailCount) {
|
|
@@ -640,10 +710,8 @@ function createSVGRenderer(options) {
|
|
|
640
710
|
const head = trail[trailCount - 1];
|
|
641
711
|
const x = px(head);
|
|
642
712
|
const y = py(head);
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
headCircle.setAttribute("cx", x);
|
|
646
|
-
headCircle.setAttribute("cy", y);
|
|
713
|
+
headCircle.setAttribute("cx", String(x));
|
|
714
|
+
headCircle.setAttribute("cy", String(y));
|
|
647
715
|
}
|
|
648
716
|
let animationId = null;
|
|
649
717
|
let lastTime = 0;
|