@sarmal/core 0.9.10 → 0.12.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/index.js CHANGED
@@ -74,9 +74,11 @@ function createEngine(curveDef, trailLength = 120) {
74
74
  const trail = new CircularBuffer(trailLength);
75
75
  let t = 0;
76
76
  let actualTime = 0;
77
+ let userSpeedOverride = null;
77
78
  let morphCurveB = null;
78
79
  let _morphAlpha = null;
79
80
  let _morphStrategy = "normalized";
81
+ let _speedTransition = null;
80
82
  function sampleSkeleton(c, sampleT) {
81
83
  if (c.skeletonFn) {
82
84
  return c.skeletonFn(sampleT);
@@ -88,9 +90,19 @@ function createEngine(curveDef, trailLength = 120) {
88
90
  }
89
91
  return {
90
92
  tick(deltaTime) {
91
- let effectiveSpeed = curve.speed;
93
+ if (_speedTransition !== null) {
94
+ _speedTransition.elapsed += deltaTime * 1e3;
95
+ const alpha = Math.min(_speedTransition.elapsed / _speedTransition.duration, 1);
96
+ userSpeedOverride = lerp(_speedTransition.from, _speedTransition.to, alpha);
97
+ if (alpha >= 1) {
98
+ userSpeedOverride = _speedTransition.to;
99
+ _speedTransition.resolve();
100
+ _speedTransition = null;
101
+ }
102
+ }
103
+ let effectiveSpeed = userSpeedOverride ?? curve.speed;
92
104
  if (morphCurveB !== null && _morphAlpha !== null) {
93
- effectiveSpeed = lerp(curve.speed, morphCurveB.speed, _morphAlpha);
105
+ effectiveSpeed = lerp(effectiveSpeed, morphCurveB.speed, _morphAlpha);
94
106
  }
95
107
  t = (t + effectiveSpeed * deltaTime) % curve.period;
96
108
  actualTime += deltaTime;
@@ -119,13 +131,13 @@ function createEngine(curveDef, trailLength = 120) {
119
131
  actualTime = 0;
120
132
  trail.clear();
121
133
  },
122
- seek(newT, { clearTrail = false } = {}) {
134
+ jump(newT, { clearTrail = false } = {}) {
123
135
  t = (newT % curve.period + curve.period) % curve.period;
124
136
  if (clearTrail) {
125
137
  trail.clear();
126
138
  }
127
139
  },
128
- seekWithTrail(targetT, { wrap = false, step = curve.period / trailLength } = {}) {
140
+ seek(targetT, { wrap = false, step = curve.period / trailLength } = {}) {
129
141
  const advance = curve.speed * step;
130
142
  const target = (targetT % curve.period + curve.period) % curve.period;
131
143
  const targetTime = target / curve.speed;
@@ -200,6 +212,44 @@ function createEngine(curveDef, trailLength = 120) {
200
212
  points[i] = sampleSkeleton(curve, sampleT);
201
213
  }
202
214
  return points;
215
+ },
216
+ setSpeed(speed) {
217
+ if (!Number.isFinite(speed)) {
218
+ throw new Error("speed must be a finite number");
219
+ }
220
+ if (_speedTransition !== null) {
221
+ _speedTransition.reject(new Error("Speed transition cancelled"));
222
+ _speedTransition = null;
223
+ }
224
+ userSpeedOverride = speed;
225
+ },
226
+ getSpeed() {
227
+ return userSpeedOverride ?? curve.speed;
228
+ },
229
+ resetSpeed() {
230
+ userSpeedOverride = null;
231
+ },
232
+ setSpeedOver(speed, duration) {
233
+ if (!Number.isFinite(speed)) {
234
+ throw new Error("speed must be a finite number");
235
+ }
236
+ if (!Number.isFinite(duration) || duration <= 0) {
237
+ throw new Error("duration must be a finite number greater than 0");
238
+ }
239
+ if (_speedTransition !== null) {
240
+ _speedTransition.reject(new Error("Speed transition cancelled"));
241
+ _speedTransition = null;
242
+ }
243
+ const from = userSpeedOverride ?? curve.speed;
244
+ return new Promise((resolve, reject) => {
245
+ _speedTransition = { from, to: speed, elapsed: 0, duration, resolve, reject };
246
+ });
247
+ },
248
+ cancelSpeedTransition() {
249
+ if (_speedTransition !== null) {
250
+ _speedTransition.reject(new Error("Speed transition cancelled"));
251
+ _speedTransition = null;
252
+ }
203
253
  }
204
254
  };
205
255
  }
@@ -299,9 +349,18 @@ function computeBoundaries(pts, logicalWidth, logicalHeight) {
299
349
  offsetY: (logicalHeight - h * scale) / 2 - minY * scale
300
350
  };
301
351
  }
352
+ function enginePassthroughs(engine) {
353
+ return {
354
+ jump: engine.jump,
355
+ seek: engine.seek,
356
+ setSpeed: engine.setSpeed,
357
+ getSpeed: engine.getSpeed,
358
+ resetSpeed: engine.resetSpeed,
359
+ setSpeedOver: engine.setSpeedOver
360
+ };
361
+ }
302
362
 
303
363
  // src/renderer.ts
304
- var DEFAULT_HEAD_RADIUS = 4;
305
364
  var DEFAULT_SKELETON_COLOR = "#ffffff";
306
365
  var GRADIENT = {
307
366
  bard: ["#a855f7", "#3b82f6", "#14b8a6", "#ec4899"],
@@ -361,14 +420,21 @@ function createRenderer(options) {
361
420
  }
362
421
  const ctx = canvas.getContext("2d");
363
422
  const engine = options.engine;
423
+ const trailStyle = options.trailStyle ?? "default";
424
+ const trailColor = options.trailColor ?? "#ffffff";
425
+ const palette = resolvePalette(options.palette, trailStyle);
426
+ function defaultHeadColor() {
427
+ if (trailStyle !== "default") {
428
+ const { r, g, b } = getPaletteColor(palette, 1);
429
+ return `rgb(${r},${g},${b})`;
430
+ }
431
+ return trailColor;
432
+ }
364
433
  const opts = {
365
434
  skeletonColor: options.skeletonColor ?? DEFAULT_SKELETON_COLOR,
366
- trailColor: options.trailColor ?? "#ffffff",
367
- headColor: options.headColor ?? "#ffffff",
368
- headRadius: options.headRadius ?? DEFAULT_HEAD_RADIUS
435
+ trailColor,
436
+ headColor: options.headColor ?? defaultHeadColor()
369
437
  };
370
- const trailStyle = options.trailStyle ?? "default";
371
- const palette = resolvePalette(options.palette, trailStyle);
372
438
  const trailRgb = hexToRgbComponents(opts.trailColor);
373
439
  const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
374
440
  function setupCanvas() {
@@ -492,9 +558,10 @@ function createRenderer(options) {
492
558
  }
493
559
  const x = head.x * scale + offsetX;
494
560
  const y = head.y * scale + offsetY;
561
+ const r = options.headRadius ?? Math.max(2, 3 * Math.sqrt(Math.min(logicalWidth, logicalHeight) / 160));
495
562
  ctx.fillStyle = opts.headColor;
496
563
  ctx.beginPath();
497
- ctx.arc(x, y, opts.headRadius, 0, Math.PI * 2);
564
+ ctx.arc(x, y, r, 0, Math.PI * 2);
498
565
  ctx.fill();
499
566
  }
500
567
  function render() {
@@ -557,6 +624,7 @@ function createRenderer(options) {
557
624
  }
558
625
  cancelAnimationFrame(animationId);
559
626
  animationId = null;
627
+ engine.cancelSpeedTransition();
560
628
  },
561
629
  reset() {
562
630
  engine.reset();
@@ -569,12 +637,7 @@ function createRenderer(options) {
569
637
  animationId = null;
570
638
  }
571
639
  },
572
- seek(t, options2) {
573
- engine.seek(t, options2);
574
- },
575
- seekWithTrail(t) {
576
- engine.seekWithTrail(t);
577
- },
640
+ ...enginePassthroughs(engine),
578
641
  morphTo(target, options2) {
579
642
  if (morphResolve !== null) {
580
643
  engine.completeMorph();
@@ -620,16 +683,17 @@ function el(tag) {
620
683
  }
621
684
  function createSVGRenderer(options) {
622
685
  const { container, engine } = options;
686
+ const trailColor = options.trailColor ?? "#ffffff";
623
687
  const opts = {
624
688
  skeletonColor: options.skeletonColor ?? "#ffffff",
625
- trailColor: options.trailColor ?? "#ffffff",
626
- headColor: options.headColor ?? "#ffffff",
627
- headRadius: options.headRadius ?? 4,
689
+ trailColor,
690
+ headColor: options.headColor ?? trailColor,
628
691
  ariaLabel: options.ariaLabel ?? "Loading"
629
692
  };
630
693
  const rect = container.getBoundingClientRect();
631
694
  const width = rect.width || 200;
632
695
  const height = rect.height || 200;
696
+ const headRadius = options.headRadius ?? Math.max(2, 3 * Math.sqrt(Math.min(width, height) / 160));
633
697
  const svg = el("svg");
634
698
  svg.setAttribute("width", String(width));
635
699
  svg.setAttribute("height", String(height));
@@ -668,7 +732,7 @@ function createSVGRenderer(options) {
668
732
  }
669
733
  const headCircle = el("circle");
670
734
  headCircle.setAttribute("fill", opts.headColor);
671
- headCircle.setAttribute("r", String(opts.headRadius));
735
+ headCircle.setAttribute("r", String(headRadius));
672
736
  svg.appendChild(headCircle);
673
737
  container.appendChild(svg);
674
738
  let scale = 1;
@@ -798,6 +862,7 @@ function createSVGRenderer(options) {
798
862
  }
799
863
  cancelAnimationFrame(animationId);
800
864
  animationId = null;
865
+ engine.cancelSpeedTransition();
801
866
  },
802
867
  reset() {
803
868
  engine.reset();
@@ -809,12 +874,7 @@ function createSVGRenderer(options) {
809
874
  }
810
875
  svg.remove();
811
876
  },
812
- seek(t, options2) {
813
- engine.seek(t, options2);
814
- },
815
- seekWithTrail(t) {
816
- engine.seekWithTrail(t);
817
- },
877
+ ...enginePassthroughs(engine),
818
878
  morphTo(target, options2) {
819
879
  if (morphResolve !== null) {
820
880
  engine.completeMorph();