@sarmal/core 0.35.0 → 0.35.1

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/README.md CHANGED
@@ -4,9 +4,11 @@
4
4
  <strong>Parametric curve animations for loading/thinking indicators</strong>
5
5
  </p>
6
6
 
7
- <p align="center">
8
- <a href="https://sarmal.art" target="_blank">Live Demo at sarmal.art</a>
9
- </p>
7
+ <div align="center">
8
+ <a href="https://sarmal.art">
9
+ <img src="../../docs/public/curves-light.gif" alt="Sarmal demo" width="100%" max-width="720">
10
+ </a>
11
+ </div>
10
12
 
11
13
  ---
12
14
 
package/dist/index.cjs CHANGED
@@ -1441,14 +1441,8 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1441
1441
  const cellW = W / cols;
1442
1442
  const cellH = H / rows;
1443
1443
  const dotR = Math.min(cellW, cellH) * 0.36;
1444
- let gradientRgb;
1445
- if (Array.isArray(initialColor)) {
1446
- validateBaseRenderOptions({ trailColor: initialColor });
1447
- gradientRgb = initialColor.map(colorToRgb);
1448
- } else {
1449
- gradientRgb = null;
1450
- }
1451
- let colorRgb = gradientRgb ? gradientRgb[0] : colorToRgb(initialColor);
1444
+ let gradientOklab = null;
1445
+ let colorRgb = { r: 255, g: 255, b: 255 };
1452
1446
  let currentTrailStyle = initialTrailStyle;
1453
1447
  let animTime = 0;
1454
1448
  const ANIM_PERIOD = 6;
@@ -1520,7 +1514,7 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1520
1514
  }
1521
1515
  function buildBgImageData() {
1522
1516
  bgImageData = new ImageData(W, H);
1523
- const bg = gradientRgb ? gradientRgb[0] : colorRgb;
1517
+ const bg = colorRgb;
1524
1518
  const baseAlpha = 0.05 * 255;
1525
1519
  const { data } = bgImageData;
1526
1520
  const n = cols * rows;
@@ -1537,18 +1531,14 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1537
1531
  }
1538
1532
  }
1539
1533
  }
1540
- function sampleGradientRgb(stops, t) {
1541
- const n = stops.length;
1542
- const scaled = Math.max(0, Math.min(1, t)) * (n - 1);
1543
- const i = Math.min(Math.floor(scaled), n - 2);
1544
- const a = stops[i];
1545
- const bStop = stops[i + 1];
1546
- const mix = scaled - i;
1547
- return {
1548
- r: Math.round(a.r + (bStop.r - a.r) * mix),
1549
- g: Math.round(a.g + (bStop.g - a.g) * mix),
1550
- b: Math.round(a.b + (bStop.b - a.b) * mix)
1551
- };
1534
+ function applyColor(color) {
1535
+ if (Array.isArray(color)) {
1536
+ gradientOklab = color.map((c) => parseColorToOklab(c));
1537
+ colorRgb = oklabToRgb(gradientOklab[0]);
1538
+ } else {
1539
+ gradientOklab = null;
1540
+ colorRgb = colorToRgb(color);
1541
+ }
1552
1542
  }
1553
1543
  function calculateBoundaries(skel) {
1554
1544
  const b = computeBoundaries(skel, W, H);
@@ -1602,7 +1592,7 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1602
1592
  }
1603
1593
  frameImageData.data.set(bgImageData.data);
1604
1594
  const { data } = frameImageData;
1605
- const sineOffset = currentTrailStyle === "gradient-animated" ? 0.15 * Math.sin(animTime / ANIM_PERIOD * 2 * Math.PI) : 0;
1595
+ const timeOffset = currentTrailStyle === "gradient-animated" ? animTime / ANIM_PERIOD : 0;
1606
1596
  const n = cols * rows;
1607
1597
  for (let dotIdx = 0; dotIdx < n; dotIdx++) {
1608
1598
  const intensity = grid[dotIdx];
@@ -1610,9 +1600,8 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1610
1600
  continue;
1611
1601
  }
1612
1602
  let r, g, b;
1613
- if (gradientRgb !== null) {
1614
- const t = Math.max(0, Math.min(1, intensity + sineOffset));
1615
- ({ r, g, b } = sampleGradientRgb(gradientRgb, t));
1603
+ if (gradientOklab !== null) {
1604
+ ({ r, g, b } = oklabToRgb(getPaletteColor(gradientOklab, intensity, timeOffset)));
1616
1605
  } else {
1617
1606
  ({ r, g, b } = colorRgb);
1618
1607
  }
@@ -1630,17 +1619,20 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1630
1619
  }
1631
1620
  ctx.putImageData(frameImageData, 0, 0);
1632
1621
  }
1622
+ function completeMorphNow() {
1623
+ engine.completeMorph();
1624
+ morphResolve?.();
1625
+ morphResolve = null;
1626
+ morphReject = null;
1627
+ morphProgress = 0;
1628
+ }
1633
1629
  function renderFrame(deltaTime) {
1634
1630
  if (engine.morphAlpha !== null) {
1635
1631
  morphProgress = Math.min(1, morphProgress + deltaTime / (morphDurationMs / 1e3));
1636
1632
  engine.setMorphAlpha(morphProgress);
1637
1633
  calculateBoundaries(engine.getSarmalSkeleton());
1638
1634
  if (morphProgress >= 1) {
1639
- engine.completeMorph();
1640
- morphResolve?.();
1641
- morphResolve = null;
1642
- morphReject = null;
1643
- morphProgress = 0;
1635
+ completeMorphNow();
1644
1636
  calculateBoundaries(engine.getSarmalSkeleton());
1645
1637
  }
1646
1638
  } else if (engine.isLiveSkeleton) {
@@ -1658,6 +1650,8 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1658
1650
  renderFrame(deltaTime);
1659
1651
  animationId = requestAnimationFrame(loop);
1660
1652
  }
1653
+ validateBaseRenderOptions({ trailColor: initialColor });
1654
+ applyColor(initialColor);
1661
1655
  calculateBoundaries(engine.getSarmalSkeleton());
1662
1656
  computePixelMask();
1663
1657
  frameImageData = new ImageData(W, H);
@@ -1706,11 +1700,7 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1706
1700
  */
1707
1701
  morphTo(target, opts) {
1708
1702
  if (morphResolve !== null) {
1709
- engine.completeMorph();
1710
- morphResolve();
1711
- morphResolve = null;
1712
- morphReject = null;
1713
- morphProgress = 0;
1703
+ completeMorphNow();
1714
1704
  }
1715
1705
  morphDurationMs = opts?.duration ?? DEFAULT_MORPH_DURATION_MS;
1716
1706
  morphProgress = 0;
@@ -1731,13 +1721,7 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1731
1721
  validateBaseRenderOptions(partial);
1732
1722
  let needsRebuildBg = false;
1733
1723
  if (partial.trailColor !== void 0) {
1734
- if (Array.isArray(partial.trailColor)) {
1735
- gradientRgb = partial.trailColor.map(colorToRgb);
1736
- colorRgb = gradientRgb[0];
1737
- } else {
1738
- gradientRgb = null;
1739
- colorRgb = colorToRgb(partial.trailColor);
1740
- }
1724
+ applyColor(partial.trailColor);
1741
1725
  needsRebuildBg = true;
1742
1726
  }
1743
1727
  if (partial.trailStyle !== void 0) {
@@ -1749,9 +1733,13 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1749
1733
  if (needsRebuildBg) {
1750
1734
  buildBgImageData();
1751
1735
  }
1752
- if (currentTrailStyle !== "default" && gradientRgb === null) {
1736
+ if (currentTrailStyle !== "default" && gradientOklab === null) {
1737
+ console.warn(
1738
+ `[sarmal] dot matrix: trailColor is a single color but trailStyle is "${currentTrailStyle}"; the trail will render as a solid color. Pass an array of hex colors to use a real gradient.`
1739
+ );
1740
+ } else if (currentTrailStyle === "default" && gradientOklab !== null) {
1753
1741
  console.warn(
1754
- "[sarmal] dot matrix: gradient trailStyle has no effect without a trailColor array"
1742
+ '[sarmal] dot matrix: trailColor is an array but trailStyle is "default"; only the first color will be used. Pass a gradient trailStyle to use the whole palette.'
1755
1743
  );
1756
1744
  }
1757
1745
  }