@sarmal/core 0.18.0 → 0.20.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/README.md +1 -12
- package/dist/auto-init.cjs +379 -20
- package/dist/auto-init.cjs.map +1 -1
- package/dist/auto-init.d.cts +4 -0
- package/dist/auto-init.d.ts +4 -0
- package/dist/auto-init.js +379 -20
- package/dist/auto-init.js.map +1 -1
- package/dist/index.cjs +85 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -8
- package/dist/index.d.ts +53 -8
- package/dist/index.js +85 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -62,18 +62,7 @@ Or with **auto-init** without having to write any JS:
|
|
|
62
62
|
|
|
63
63
|
## Standard Curves
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
| -------------- | ------------------------------------------ |
|
|
67
|
-
| `artemis2` | Artemis II free-return lunar trajectory |
|
|
68
|
-
| `epitrochoid7` | 7-lobed epitrochoid with dynamic variation |
|
|
69
|
-
| `astroid` | 4-cusped astroid |
|
|
70
|
-
| `deltoid` | 3-cusped deltoid |
|
|
71
|
-
| `rose5` | 5-petal rose curve |
|
|
72
|
-
| `rose3` | 3-petal rose curve |
|
|
73
|
-
| `lissajous32` | Lissajous 3:2 with live skeleton |
|
|
74
|
-
| `lissajous43` | Lissajous 4:3 with live skeleton |
|
|
75
|
-
| `epicycloid3` | 3-cusped epicycloid |
|
|
76
|
-
| `lame` | Lamé curve with live skeleton |
|
|
65
|
+
See [sarmal.art/docs#curves](https://sarmal.art/docs#curves) for the full list of built-in curves.
|
|
77
66
|
|
|
78
67
|
## Documentation
|
|
79
68
|
|
package/dist/auto-init.cjs
CHANGED
|
@@ -300,12 +300,20 @@ function computeNormal(trail, i) {
|
|
|
300
300
|
const tangent = computeTangent(trail, i);
|
|
301
301
|
return { x: -tangent.y, y: tangent.x };
|
|
302
302
|
}
|
|
303
|
-
function computeTrailQuad(
|
|
303
|
+
function computeTrailQuad(
|
|
304
|
+
trail,
|
|
305
|
+
i,
|
|
306
|
+
trailCount,
|
|
307
|
+
toX,
|
|
308
|
+
toY,
|
|
309
|
+
minWidth = TRAIL_MIN_WIDTH,
|
|
310
|
+
maxWidth = TRAIL_MAX_WIDTH,
|
|
311
|
+
) {
|
|
304
312
|
const progress = i / (trailCount - 1);
|
|
305
313
|
const nextProgress = (i + 1) / (trailCount - 1);
|
|
306
314
|
const opacity = Math.pow(progress, TRAIL_FADE_CURVE) * TRAIL_MAX_OPACITY;
|
|
307
|
-
const w0 = (
|
|
308
|
-
const w1 = (
|
|
315
|
+
const w0 = (minWidth + progress * (maxWidth - minWidth)) / 2;
|
|
316
|
+
const w1 = (minWidth + nextProgress * (maxWidth - minWidth)) / 2;
|
|
309
317
|
const curr = trail[i];
|
|
310
318
|
const next = trail[i + 1];
|
|
311
319
|
const n0 = computeNormal(trail, i);
|
|
@@ -816,6 +824,350 @@ function createRenderer(options) {
|
|
|
816
824
|
return instance;
|
|
817
825
|
}
|
|
818
826
|
|
|
827
|
+
// src/renderer-svg.ts
|
|
828
|
+
var EMPTY_PARAMS2 = {};
|
|
829
|
+
var HIGH_TRAIL_LENGTH_THRESHOLD = 5e3;
|
|
830
|
+
function pointsToPathString(pts, scale, offsetX, offsetY) {
|
|
831
|
+
if (pts.length < 2) {
|
|
832
|
+
return "";
|
|
833
|
+
}
|
|
834
|
+
const px = (p) => (p.x * scale + offsetX).toFixed(2);
|
|
835
|
+
const py = (p) => (p.y * scale + offsetY).toFixed(2);
|
|
836
|
+
let d = `M${px(pts[0])} ${py(pts[0])}`;
|
|
837
|
+
for (let i = 1; i < pts.length; i++) {
|
|
838
|
+
d += ` L${px(pts[i])} ${py(pts[i])}`;
|
|
839
|
+
}
|
|
840
|
+
return d + " Z";
|
|
841
|
+
}
|
|
842
|
+
function sampleCurveSkeleton(curveDef) {
|
|
843
|
+
const period = curveDef.period ?? Math.PI * 2;
|
|
844
|
+
const samples = Math.ceil(period * 50);
|
|
845
|
+
const pts = Array.from({ length: samples });
|
|
846
|
+
for (let i = 0; i < samples; i++) {
|
|
847
|
+
const t = (i / (samples - 1)) * period;
|
|
848
|
+
pts[i] = curveDef.skeletonFn ? curveDef.skeletonFn(t) : curveDef.fn(t, 0, EMPTY_PARAMS2);
|
|
849
|
+
}
|
|
850
|
+
return pts;
|
|
851
|
+
}
|
|
852
|
+
function el(tag) {
|
|
853
|
+
return document.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
854
|
+
}
|
|
855
|
+
function createSVGRenderer(options) {
|
|
856
|
+
const { container, engine } = options;
|
|
857
|
+
const poolSize = engine.trailLength;
|
|
858
|
+
if (poolSize > HIGH_TRAIL_LENGTH_THRESHOLD) {
|
|
859
|
+
console.warn(
|
|
860
|
+
`[sarmal] High trailLength in SVG renderer (${poolSize}). Consider using the canvas renderer for long trails.`,
|
|
861
|
+
);
|
|
862
|
+
}
|
|
863
|
+
let trailStyle = options.trailStyle ?? "default";
|
|
864
|
+
let trailColor = options.trailColor ?? "#ffffff";
|
|
865
|
+
let skeletonColor = options.skeletonColor ?? "#ffffff";
|
|
866
|
+
let userHeadColor = options.headColor ?? null;
|
|
867
|
+
let headColor = userHeadColor ?? resolveHeadColor(trailColor, trailStyle);
|
|
868
|
+
let trailSolid = resolveTrailMainColor(trailColor);
|
|
869
|
+
let trailPalette = resolveTrailPalette(trailColor);
|
|
870
|
+
const ariaLabel = options.ariaLabel ?? "Loading";
|
|
871
|
+
warnIfTrailColorMismatch(trailColor, trailStyle);
|
|
872
|
+
const viewSize = 100;
|
|
873
|
+
const headRadius = options.headRadius ?? 1.5;
|
|
874
|
+
const svgTrailMinWidth = 0.25;
|
|
875
|
+
const svgTrailMaxWidth = 1.25;
|
|
876
|
+
const svgSkeletonStrokeWidth = "0.75";
|
|
877
|
+
container.setAttribute("viewBox", `0 0 ${viewSize} ${viewSize}`);
|
|
878
|
+
container.setAttribute("role", "img");
|
|
879
|
+
container.setAttribute("aria-label", ariaLabel);
|
|
880
|
+
const group = el("g");
|
|
881
|
+
const titleEl = el("title");
|
|
882
|
+
titleEl.textContent = ariaLabel;
|
|
883
|
+
group.appendChild(titleEl);
|
|
884
|
+
const skeletonPath = el("path");
|
|
885
|
+
skeletonPath.setAttribute("data-sarmal-role", "skeleton");
|
|
886
|
+
skeletonPath.setAttribute("fill", "none");
|
|
887
|
+
skeletonPath.setAttribute("stroke", skeletonColor);
|
|
888
|
+
skeletonPath.setAttribute("stroke-opacity", String(DEFAULT_SKELETON_OPACITY));
|
|
889
|
+
skeletonPath.setAttribute("stroke-width", svgSkeletonStrokeWidth);
|
|
890
|
+
if (skeletonColor === "transparent") {
|
|
891
|
+
skeletonPath.setAttribute("visibility", "hidden");
|
|
892
|
+
}
|
|
893
|
+
group.appendChild(skeletonPath);
|
|
894
|
+
const skeletonPathA = el("path");
|
|
895
|
+
skeletonPathA.setAttribute("fill", "none");
|
|
896
|
+
skeletonPathA.setAttribute("stroke", skeletonColor);
|
|
897
|
+
skeletonPathA.setAttribute("stroke-width", svgSkeletonStrokeWidth);
|
|
898
|
+
skeletonPathA.setAttribute("visibility", "hidden");
|
|
899
|
+
group.appendChild(skeletonPathA);
|
|
900
|
+
const skeletonPathB = el("path");
|
|
901
|
+
skeletonPathB.setAttribute("fill", "none");
|
|
902
|
+
skeletonPathB.setAttribute("stroke", skeletonColor);
|
|
903
|
+
skeletonPathB.setAttribute("stroke-width", svgSkeletonStrokeWidth);
|
|
904
|
+
skeletonPathB.setAttribute("visibility", "hidden");
|
|
905
|
+
group.appendChild(skeletonPathB);
|
|
906
|
+
let morphPathABuilt = "";
|
|
907
|
+
let morphPathBBuilt = "";
|
|
908
|
+
const trailPaths = [];
|
|
909
|
+
for (let i = 0; i < poolSize; i++) {
|
|
910
|
+
const path = el("path");
|
|
911
|
+
path.setAttribute("fill", trailSolid);
|
|
912
|
+
group.appendChild(path);
|
|
913
|
+
trailPaths.push(path);
|
|
914
|
+
}
|
|
915
|
+
const headCircle = el("circle");
|
|
916
|
+
headCircle.setAttribute("data-sarmal-role", "head");
|
|
917
|
+
headCircle.setAttribute("fill", headColor);
|
|
918
|
+
headCircle.setAttribute("r", String(headRadius));
|
|
919
|
+
group.appendChild(headCircle);
|
|
920
|
+
container.appendChild(group);
|
|
921
|
+
let gradientAnimTime = 0;
|
|
922
|
+
let scale = 1;
|
|
923
|
+
let offsetX = 0;
|
|
924
|
+
let offsetY = 0;
|
|
925
|
+
function applyBoundaries(skeleton2) {
|
|
926
|
+
const b = computeBoundaries(skeleton2, viewSize, viewSize);
|
|
927
|
+
if (b) {
|
|
928
|
+
scale = b.scale;
|
|
929
|
+
offsetX = b.offsetX;
|
|
930
|
+
offsetY = b.offsetY;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
function px(p) {
|
|
934
|
+
return p.x * scale + offsetX;
|
|
935
|
+
}
|
|
936
|
+
function py(p) {
|
|
937
|
+
return p.y * scale + offsetY;
|
|
938
|
+
}
|
|
939
|
+
function updateSkeleton(skeleton2) {
|
|
940
|
+
skeletonPath.setAttribute("d", pointsToPathString(skeleton2, scale, offsetX, offsetY));
|
|
941
|
+
}
|
|
942
|
+
const skeleton = engine.getSarmalSkeleton();
|
|
943
|
+
applyBoundaries(skeleton);
|
|
944
|
+
if (!engine.isLiveSkeleton) {
|
|
945
|
+
updateSkeleton(skeleton);
|
|
946
|
+
}
|
|
947
|
+
function updateTrail(trail, trailCount) {
|
|
948
|
+
if (trailCount < 2) {
|
|
949
|
+
for (const p of trailPaths) {
|
|
950
|
+
p.setAttribute("d", "");
|
|
951
|
+
}
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
const drawnCount = trailCount - 1;
|
|
955
|
+
for (let i = 0; i < drawnCount; i++) {
|
|
956
|
+
const { l0x, l0y, r0x, r0y, l1x, l1y, r1x, r1y, opacity, progress } = computeTrailQuad(
|
|
957
|
+
trail,
|
|
958
|
+
i,
|
|
959
|
+
trailCount,
|
|
960
|
+
px,
|
|
961
|
+
py,
|
|
962
|
+
svgTrailMinWidth,
|
|
963
|
+
svgTrailMaxWidth,
|
|
964
|
+
);
|
|
965
|
+
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`;
|
|
966
|
+
trailPaths[i].setAttribute("d", d);
|
|
967
|
+
trailPaths[i].setAttribute("fill-opacity", opacity.toFixed(3));
|
|
968
|
+
if (trailStyle !== "default") {
|
|
969
|
+
const timeOffset = trailStyle === "gradient-animated" ? gradientAnimTime * 5e-4 : 0;
|
|
970
|
+
const { r, g, b } = getPaletteColor(trailPalette, progress, timeOffset);
|
|
971
|
+
trailPaths[i].setAttribute("fill", `rgb(${r},${g},${b})`);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
for (let i = drawnCount; i < trailPaths.length; i++) {
|
|
975
|
+
trailPaths[i].setAttribute("d", "");
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
function updateHead(trail, trailCount) {
|
|
979
|
+
if (trailCount === 0) {
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
const head = trail[trailCount - 1];
|
|
983
|
+
const x = px(head);
|
|
984
|
+
const y = py(head);
|
|
985
|
+
headCircle.setAttribute("cx", String(x));
|
|
986
|
+
headCircle.setAttribute("cy", String(y));
|
|
987
|
+
}
|
|
988
|
+
let animationId = null;
|
|
989
|
+
let lastTime = 0;
|
|
990
|
+
const prefersReducedMotion =
|
|
991
|
+
typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
992
|
+
let morphResolve = null;
|
|
993
|
+
let morphReject = null;
|
|
994
|
+
let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
|
|
995
|
+
let morphTarget = null;
|
|
996
|
+
let morphAlpha = 0;
|
|
997
|
+
function renderFrame(deltaTime) {
|
|
998
|
+
if (trailStyle === "gradient-animated") {
|
|
999
|
+
gradientAnimTime += deltaTime * 1e3;
|
|
1000
|
+
}
|
|
1001
|
+
if (engine.morphAlpha !== null) {
|
|
1002
|
+
morphAlpha = Math.min(1, morphAlpha + deltaTime / (morphDurationMs / 1e3));
|
|
1003
|
+
engine.setMorphAlpha(morphAlpha);
|
|
1004
|
+
if (morphPathABuilt) {
|
|
1005
|
+
skeletonPathA.setAttribute("d", morphPathABuilt);
|
|
1006
|
+
skeletonPathA.setAttribute("visibility", "visible");
|
|
1007
|
+
skeletonPathA.setAttribute(
|
|
1008
|
+
"stroke-opacity",
|
|
1009
|
+
String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY),
|
|
1010
|
+
);
|
|
1011
|
+
}
|
|
1012
|
+
if (morphPathBBuilt) {
|
|
1013
|
+
skeletonPathB.setAttribute("d", morphPathBBuilt);
|
|
1014
|
+
skeletonPathB.setAttribute("visibility", "visible");
|
|
1015
|
+
skeletonPathB.setAttribute("stroke-opacity", String(morphAlpha * DEFAULT_SKELETON_OPACITY));
|
|
1016
|
+
}
|
|
1017
|
+
if (morphAlpha >= 1) {
|
|
1018
|
+
engine.completeMorph();
|
|
1019
|
+
morphResolve?.();
|
|
1020
|
+
morphResolve = null;
|
|
1021
|
+
morphReject = null;
|
|
1022
|
+
morphTarget = null;
|
|
1023
|
+
morphAlpha = 0;
|
|
1024
|
+
morphPathABuilt = "";
|
|
1025
|
+
morphPathBBuilt = "";
|
|
1026
|
+
skeletonPathA.setAttribute("visibility", "hidden");
|
|
1027
|
+
skeletonPathB.setAttribute("visibility", "hidden");
|
|
1028
|
+
const newSkeleton = engine.getSarmalSkeleton();
|
|
1029
|
+
applyBoundaries(newSkeleton);
|
|
1030
|
+
updateSkeleton(newSkeleton);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
const trail = engine.tick(deltaTime);
|
|
1034
|
+
const trailCount = engine.trailCount;
|
|
1035
|
+
if (engine.isLiveSkeleton && engine.morphAlpha === null) {
|
|
1036
|
+
const liveSkeleton = engine.getSarmalSkeleton();
|
|
1037
|
+
applyBoundaries(liveSkeleton);
|
|
1038
|
+
updateSkeleton(liveSkeleton);
|
|
1039
|
+
}
|
|
1040
|
+
updateTrail(trail, trailCount);
|
|
1041
|
+
updateHead(trail, trailCount);
|
|
1042
|
+
}
|
|
1043
|
+
function loop() {
|
|
1044
|
+
const now = performance.now();
|
|
1045
|
+
const deltaTime = Math.min((now - lastTime) / 1e3, 1 / 30);
|
|
1046
|
+
lastTime = now;
|
|
1047
|
+
renderFrame(deltaTime);
|
|
1048
|
+
if (!prefersReducedMotion) {
|
|
1049
|
+
animationId = requestAnimationFrame(loop);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
if (options.initialT !== void 0) {
|
|
1053
|
+
engine.seek(options.initialT);
|
|
1054
|
+
}
|
|
1055
|
+
renderFrame(0);
|
|
1056
|
+
const shouldAutoStart = options.autoStart !== false;
|
|
1057
|
+
const instance = {
|
|
1058
|
+
play() {
|
|
1059
|
+
if (animationId !== null) {
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
lastTime = performance.now();
|
|
1063
|
+
loop();
|
|
1064
|
+
},
|
|
1065
|
+
pause() {
|
|
1066
|
+
if (animationId === null) {
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
cancelAnimationFrame(animationId);
|
|
1070
|
+
animationId = null;
|
|
1071
|
+
engine.cancelSpeedTransition();
|
|
1072
|
+
},
|
|
1073
|
+
reset() {
|
|
1074
|
+
engine.reset();
|
|
1075
|
+
},
|
|
1076
|
+
destroy() {
|
|
1077
|
+
if (animationId !== null) {
|
|
1078
|
+
cancelAnimationFrame(animationId);
|
|
1079
|
+
animationId = null;
|
|
1080
|
+
}
|
|
1081
|
+
if (morphReject !== null) {
|
|
1082
|
+
morphReject(new Error("Instance destroyed during morph"));
|
|
1083
|
+
morphResolve = null;
|
|
1084
|
+
morphReject = null;
|
|
1085
|
+
}
|
|
1086
|
+
group.remove();
|
|
1087
|
+
},
|
|
1088
|
+
...enginePassthroughs(engine),
|
|
1089
|
+
morphTo(target, options2) {
|
|
1090
|
+
if (morphResolve !== null) {
|
|
1091
|
+
engine.completeMorph();
|
|
1092
|
+
morphResolve();
|
|
1093
|
+
morphResolve = null;
|
|
1094
|
+
morphReject = null;
|
|
1095
|
+
morphAlpha = 0;
|
|
1096
|
+
skeletonPathA.setAttribute("visibility", "hidden");
|
|
1097
|
+
skeletonPathB.setAttribute("visibility", "hidden");
|
|
1098
|
+
}
|
|
1099
|
+
morphDurationMs = options2?.duration ?? DEFAULT_MORPH_DURATION_MS;
|
|
1100
|
+
morphTarget = target;
|
|
1101
|
+
morphAlpha = 0;
|
|
1102
|
+
const currentSkeleton = engine.getSarmalSkeleton();
|
|
1103
|
+
morphPathABuilt = pointsToPathString(currentSkeleton, scale, offsetX, offsetY);
|
|
1104
|
+
engine.startMorph(target, options2?.morphStrategy);
|
|
1105
|
+
if (morphTarget) {
|
|
1106
|
+
const targetSkeleton = sampleCurveSkeleton(target);
|
|
1107
|
+
morphPathBBuilt = pointsToPathString(targetSkeleton, scale, offsetX, offsetY);
|
|
1108
|
+
}
|
|
1109
|
+
return new Promise((resolve, reject) => {
|
|
1110
|
+
morphResolve = resolve;
|
|
1111
|
+
morphReject = reject;
|
|
1112
|
+
});
|
|
1113
|
+
},
|
|
1114
|
+
setRenderOptions(partial) {
|
|
1115
|
+
validateRenderOptions(partial);
|
|
1116
|
+
const prevTrailStyle = trailStyle;
|
|
1117
|
+
if (partial.trailColor !== void 0) {
|
|
1118
|
+
trailColor = partial.trailColor;
|
|
1119
|
+
trailSolid = resolveTrailMainColor(trailColor);
|
|
1120
|
+
trailPalette = resolveTrailPalette(trailColor);
|
|
1121
|
+
if (trailStyle === "default") {
|
|
1122
|
+
for (const p of trailPaths) {
|
|
1123
|
+
p.setAttribute("fill", trailSolid);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
if (partial.skeletonColor !== void 0) {
|
|
1128
|
+
skeletonColor = partial.skeletonColor;
|
|
1129
|
+
if (skeletonColor === "transparent") {
|
|
1130
|
+
skeletonPath.setAttribute("visibility", "hidden");
|
|
1131
|
+
} else {
|
|
1132
|
+
skeletonPath.setAttribute("stroke", skeletonColor);
|
|
1133
|
+
skeletonPath.removeAttribute("visibility");
|
|
1134
|
+
skeletonPathA.setAttribute("stroke", skeletonColor);
|
|
1135
|
+
skeletonPathB.setAttribute("stroke", skeletonColor);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
if (partial.trailStyle !== void 0) {
|
|
1139
|
+
trailStyle = partial.trailStyle;
|
|
1140
|
+
if (prevTrailStyle !== "default" && trailStyle === "default") {
|
|
1141
|
+
for (const p of trailPaths) {
|
|
1142
|
+
p.setAttribute("fill", trailSolid);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
if (partial.headColor !== void 0) {
|
|
1147
|
+
userHeadColor = partial.headColor;
|
|
1148
|
+
}
|
|
1149
|
+
if (userHeadColor === null) {
|
|
1150
|
+
headColor = resolveHeadColor(trailColor, trailStyle);
|
|
1151
|
+
} else {
|
|
1152
|
+
headColor = userHeadColor;
|
|
1153
|
+
}
|
|
1154
|
+
headCircle.setAttribute("fill", headColor);
|
|
1155
|
+
if (partial.trailColor !== void 0 || partial.trailStyle !== void 0) {
|
|
1156
|
+
warnIfTrailColorMismatch(trailColor, trailStyle);
|
|
1157
|
+
}
|
|
1158
|
+
},
|
|
1159
|
+
};
|
|
1160
|
+
if (shouldAutoStart) {
|
|
1161
|
+
instance.play();
|
|
1162
|
+
}
|
|
1163
|
+
return instance;
|
|
1164
|
+
}
|
|
1165
|
+
function createSarmalSVG(container, curveDef, options) {
|
|
1166
|
+
const { trailLength, ...rendererOpts } = options ?? {};
|
|
1167
|
+
const engine = createEngine(curveDef, trailLength);
|
|
1168
|
+
return createSVGRenderer({ container, engine, ...rendererOpts });
|
|
1169
|
+
}
|
|
1170
|
+
|
|
819
1171
|
// src/curves/artemis2.ts
|
|
820
1172
|
var TWO_PI2 = Math.PI * 2;
|
|
821
1173
|
function artemis2Fn(t, _time, _params) {
|
|
@@ -1101,10 +1453,24 @@ function parseTrailColor(value) {
|
|
|
1101
1453
|
} catch {}
|
|
1102
1454
|
return value;
|
|
1103
1455
|
}
|
|
1456
|
+
function buildOptions(el2) {
|
|
1457
|
+
return {
|
|
1458
|
+
...(el2.dataset.trailColor && {
|
|
1459
|
+
trailColor: parseTrailColor(el2.dataset.trailColor),
|
|
1460
|
+
}),
|
|
1461
|
+
...(el2.dataset.skeletonColor && { skeletonColor: el2.dataset.skeletonColor }),
|
|
1462
|
+
...(el2.dataset.headColor && { headColor: el2.dataset.headColor }),
|
|
1463
|
+
...(el2.dataset.headRadius && { headRadius: parseFloat(el2.dataset.headRadius) }),
|
|
1464
|
+
...(el2.dataset.trailLength && { trailLength: parseInt(el2.dataset.trailLength, 10) }),
|
|
1465
|
+
...(el2.dataset.trailStyle && {
|
|
1466
|
+
trailStyle: el2.dataset.trailStyle,
|
|
1467
|
+
}),
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1104
1470
|
function init() {
|
|
1105
|
-
const
|
|
1106
|
-
|
|
1107
|
-
const curveName =
|
|
1471
|
+
const elements = document.querySelectorAll("canvas[data-sarmal], svg[data-sarmal]");
|
|
1472
|
+
elements.forEach((el2) => {
|
|
1473
|
+
const curveName = el2.getAttribute("data-sarmal");
|
|
1108
1474
|
if (curveName == null) {
|
|
1109
1475
|
return console.warn("[sarmal] curveName is required");
|
|
1110
1476
|
}
|
|
@@ -1112,20 +1478,13 @@ function init() {
|
|
|
1112
1478
|
if (!curveDef) {
|
|
1113
1479
|
return console.error(`[sarmal] "${curveName}" is not a valid curve name`);
|
|
1114
1480
|
}
|
|
1115
|
-
const
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
...(canvas.dataset.trailLength && { trailLength: parseInt(canvas.dataset.trailLength, 10) }),
|
|
1123
|
-
...(canvas.dataset.trailStyle && {
|
|
1124
|
-
trailStyle: canvas.dataset.trailStyle,
|
|
1125
|
-
}),
|
|
1126
|
-
});
|
|
1127
|
-
if (canvas.dataset.speed) {
|
|
1128
|
-
instance.setSpeed(parseFloat(canvas.dataset.speed));
|
|
1481
|
+
const options = buildOptions(el2);
|
|
1482
|
+
const instance =
|
|
1483
|
+
el2 instanceof HTMLCanvasElement
|
|
1484
|
+
? createSarmal(el2, curveDef, options)
|
|
1485
|
+
: createSarmalSVG(el2, curveDef, options);
|
|
1486
|
+
if (el2.dataset.speed) {
|
|
1487
|
+
instance.setSpeed(parseFloat(el2.dataset.speed));
|
|
1129
1488
|
}
|
|
1130
1489
|
});
|
|
1131
1490
|
}
|