@sebastianwessel/isostate 0.1.2 → 0.2.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/animation/controller.d.ts +25 -1
- package/dist/animation/controller.d.ts.map +1 -1
- package/dist/browser/isostate.runtime.js +5 -2459
- package/dist/browser/isostate.runtime.js.map +21 -1
- package/dist/browser-runtime.d.ts +5 -0
- package/dist/browser-runtime.d.ts.map +1 -0
- package/dist/chunks/{index-DEv2-G9F.js → index-DoyWxJDm.js} +303 -13
- package/dist/chunks/index-DoyWxJDm.js.map +1 -0
- package/dist/dsl/index.js +111 -2
- package/dist/dsl/index.js.map +1 -1
- package/dist/dsl/scene-parser.d.ts.map +1 -1
- package/dist/dsl/scene-validator.d.ts +2 -1
- package/dist/dsl/scene-validator.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/rendering/rendering-engine.d.ts +15 -1
- package/dist/rendering/rendering-engine.d.ts.map +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/mount-scene.d.ts +9 -0
- package/dist/runtime/mount-scene.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/node.d.ts +39 -0
- package/dist/types/node.d.ts.map +1 -1
- package/dist/types/scene.d.ts +3 -1
- package/dist/types/scene.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunks/index-DEv2-G9F.js.map +0 -1
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { mountScene } from "./runtime/mount-scene.ts";
|
|
2
|
+
import { projectToScreen as projectGridToScreen } from "./utils/projection.ts";
|
|
3
|
+
export { mountScene };
|
|
4
|
+
export declare function projectToScreen(x: number, y: number, cellSize: number, offsetX?: number, offsetY?: number, paddingX?: number, paddingY?: number): ReturnType<typeof projectGridToScreen>;
|
|
5
|
+
//# sourceMappingURL=browser-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-runtime.d.ts","sourceRoot":"","sources":["../src/browser-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,eAAe,IAAI,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE/E,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB,wBAAgB,eAAe,CAC9B,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,MAAM,GACf,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAExC"}
|
|
@@ -908,6 +908,7 @@ function buildSceneDOM(container, bundle, config) {
|
|
|
908
908
|
presence: "removed",
|
|
909
909
|
};
|
|
910
910
|
const instance = createElementInstance(initial, layout, assetResolver);
|
|
911
|
+
instance.anchor = assetAnchorForBounds(bundle, initial);
|
|
911
912
|
instance.node.classList.add(`iso-layer-${def.layer}`);
|
|
912
913
|
instance.node.setAttribute("data-layer", def.layer);
|
|
913
914
|
if (initial.presence === "removed") {
|
|
@@ -936,6 +937,7 @@ function updateElementTransforms(svg, elements, connectors = []) {
|
|
|
936
937
|
const state = map.get(def.id);
|
|
937
938
|
if (!state)
|
|
938
939
|
continue;
|
|
940
|
+
state.current = def;
|
|
939
941
|
updateGeneratedElementContent(state.node, def, layout);
|
|
940
942
|
applyElementTransform(state.node, def, layout);
|
|
941
943
|
applyAmbientClasses(state, def.ambient ?? []);
|
|
@@ -962,6 +964,32 @@ function getConnectorState(svg, id) {
|
|
|
962
964
|
function getResolvedViewBox(bundle) {
|
|
963
965
|
return resolveSceneLayout(bundle).viewBox;
|
|
964
966
|
}
|
|
967
|
+
function getCurrentElementBounds(svg, id) {
|
|
968
|
+
const layout = svg._layout;
|
|
969
|
+
const state = svg._elementMap?.get(id);
|
|
970
|
+
if (!layout || !state || state.current.presence === "removed")
|
|
971
|
+
return undefined;
|
|
972
|
+
return boundsToViewBox(calculateElementBounds(state.current, layout, state.anchor));
|
|
973
|
+
}
|
|
974
|
+
function getGridAreaBounds(bundle, area) {
|
|
975
|
+
const layout = resolveSceneLayout(bundle);
|
|
976
|
+
const [x, y] = area.at;
|
|
977
|
+
const [width, height] = area.size;
|
|
978
|
+
let bounds = emptyBounds();
|
|
979
|
+
for (const point of [
|
|
980
|
+
[x, y],
|
|
981
|
+
[x + width, y],
|
|
982
|
+
[x, y + height],
|
|
983
|
+
[x + width, y + height],
|
|
984
|
+
]) {
|
|
985
|
+
const screen = projectToScreen(point[0], point[1], layout.cellSize, layout.selectedBounds.minX, layout.selectedBounds.minY, layout.padding.x, layout.padding.y);
|
|
986
|
+
bounds = includePoint(bounds, screen.screenX, screen.screenY);
|
|
987
|
+
}
|
|
988
|
+
return boundsToViewBox(bounds);
|
|
989
|
+
}
|
|
990
|
+
function applySceneViewBox(svg, viewBox) {
|
|
991
|
+
svg.setAttribute("viewBox", `${viewBox.minX} ${viewBox.minY} ${viewBox.width} ${viewBox.height}`);
|
|
992
|
+
}
|
|
965
993
|
// ── Lifecycle helpers ─────────────────────────────────────────────────────
|
|
966
994
|
/** Hide an element after its exit animation completes. */
|
|
967
995
|
function hideElementAfterExit(node) {
|
|
@@ -1003,15 +1031,7 @@ function calculateContentBounds(bundle, cellSize) {
|
|
|
1003
1031
|
for (const element of stop.elements ?? []) {
|
|
1004
1032
|
if (element.presence === "removed")
|
|
1005
1033
|
continue;
|
|
1006
|
-
|
|
1007
|
-
const visualSize = calculateVisualSize(element.size, cellSize);
|
|
1008
|
-
const [anchorX, anchorY] = assetAnchorForBounds(bundle, element);
|
|
1009
|
-
bounds = includeBounds(bounds, {
|
|
1010
|
-
minX: rawX - visualSize * anchorX,
|
|
1011
|
-
minY: rawY - visualSize * anchorY,
|
|
1012
|
-
maxX: rawX + visualSize * (1 - anchorX),
|
|
1013
|
-
maxY: rawY + visualSize * (1 - anchorY),
|
|
1014
|
-
});
|
|
1034
|
+
bounds = includeBounds(bounds, calculateRawElementBounds(bundle, element, cellSize));
|
|
1015
1035
|
}
|
|
1016
1036
|
for (const connector of stop.connectors ?? []) {
|
|
1017
1037
|
if (connector.presence === "removed")
|
|
@@ -1021,6 +1041,27 @@ function calculateContentBounds(bundle, cellSize) {
|
|
|
1021
1041
|
}
|
|
1022
1042
|
return normalizeBounds(bounds, cellSize);
|
|
1023
1043
|
}
|
|
1044
|
+
function calculateRawElementBounds(bundle, element, cellSize) {
|
|
1045
|
+
const { rawX, rawY } = projectToRaw(element.pos[0] + element.size, element.pos[1] + element.size, cellSize);
|
|
1046
|
+
const visualSize = calculateVisualSize(element.size, cellSize);
|
|
1047
|
+
const [anchorX, anchorY] = assetAnchorForBounds(bundle, element);
|
|
1048
|
+
return {
|
|
1049
|
+
minX: rawX - visualSize * anchorX,
|
|
1050
|
+
minY: rawY - visualSize * anchorY,
|
|
1051
|
+
maxX: rawX + visualSize * (1 - anchorX),
|
|
1052
|
+
maxY: rawY + visualSize * (1 - anchorY),
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
function calculateElementBounds(element, layout, anchor) {
|
|
1056
|
+
const screen = projectToScreen(element.pos[0] + element.size, element.pos[1] + element.size, layout.cellSize, layout.selectedBounds.minX, layout.selectedBounds.minY, layout.padding.x, layout.padding.y);
|
|
1057
|
+
const visualSize = calculateVisualSize(element.size, layout.cellSize);
|
|
1058
|
+
return {
|
|
1059
|
+
minX: screen.screenX - visualSize * anchor[0],
|
|
1060
|
+
minY: screen.screenY - visualSize * anchor[1],
|
|
1061
|
+
maxX: screen.screenX + visualSize * (1 - anchor[0]),
|
|
1062
|
+
maxY: screen.screenY + visualSize * (1 - anchor[1]),
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1024
1065
|
function assetAnchorForBounds(bundle, element) {
|
|
1025
1066
|
return bundle.assets?.[element.asset]?.anchor ?? [0.5, 1];
|
|
1026
1067
|
}
|
|
@@ -1076,6 +1117,18 @@ function normalizeBounds(bounds, cellSize) {
|
|
|
1076
1117
|
return bounds;
|
|
1077
1118
|
return { minX: 0, minY: 0, maxX: cellSize, maxY: cellSize };
|
|
1078
1119
|
}
|
|
1120
|
+
function boundsToViewBox(bounds) {
|
|
1121
|
+
const centerX = (bounds.minX + bounds.maxX) / 2;
|
|
1122
|
+
const centerY = (bounds.minY + bounds.maxY) / 2;
|
|
1123
|
+
const width = Math.max(1, bounds.maxX - bounds.minX);
|
|
1124
|
+
const height = Math.max(1, bounds.maxY - bounds.minY);
|
|
1125
|
+
return {
|
|
1126
|
+
minX: roundDimension(centerX - width / 2),
|
|
1127
|
+
minY: roundDimension(centerY - height / 2),
|
|
1128
|
+
width: roundDimension(width),
|
|
1129
|
+
height: roundDimension(height),
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1079
1132
|
function includePoint(bounds, x, y) {
|
|
1080
1133
|
return {
|
|
1081
1134
|
minX: Math.min(bounds.minX, x),
|
|
@@ -1242,9 +1295,9 @@ function createElementInstance(def, layout, resolveAsset) {
|
|
|
1242
1295
|
node.addEventListener("animationend", () => {
|
|
1243
1296
|
node.style.animation = "";
|
|
1244
1297
|
}, { once: true });
|
|
1245
|
-
return { node, isHidden: false, entryKey: entryAnim, ambient: new Set() };
|
|
1298
|
+
return { node, current: def, anchor: [0.5, 1], isHidden: false, entryKey: entryAnim, ambient: new Set() };
|
|
1246
1299
|
}
|
|
1247
|
-
return { node, isHidden: false, ambient: new Set() };
|
|
1300
|
+
return { node, current: def, anchor: [0.5, 1], isHidden: false, ambient: new Set() };
|
|
1248
1301
|
}
|
|
1249
1302
|
function isTextAsset(assetId) {
|
|
1250
1303
|
return assetId === BUILT_IN_TEXT_ASSET_ID;
|
|
@@ -1586,6 +1639,8 @@ class AnimationController {
|
|
|
1586
1639
|
_pendingProgress = null;
|
|
1587
1640
|
_destroyed = false;
|
|
1588
1641
|
_listeners = new Map();
|
|
1642
|
+
_cameraAnim = null;
|
|
1643
|
+
_cameraState = null;
|
|
1589
1644
|
// Scroll tracking state
|
|
1590
1645
|
_minScroll = 0;
|
|
1591
1646
|
_maxScroll = 0;
|
|
@@ -1637,6 +1692,11 @@ class AnimationController {
|
|
|
1637
1692
|
this._paused = false;
|
|
1638
1693
|
this._config = { ...DEFAULT_CONFIG, ...config };
|
|
1639
1694
|
this._sceneElement = runtime.sceneElement ?? config.sceneElement ?? null;
|
|
1695
|
+
this._cameraState = {
|
|
1696
|
+
viewBox: getResolvedViewBox(bundle),
|
|
1697
|
+
target: { type: "reset" },
|
|
1698
|
+
isZoomed: false,
|
|
1699
|
+
};
|
|
1640
1700
|
this._engine.init(bundle);
|
|
1641
1701
|
this._bindScroll();
|
|
1642
1702
|
}
|
|
@@ -1653,6 +1713,7 @@ class AnimationController {
|
|
|
1653
1713
|
return;
|
|
1654
1714
|
}
|
|
1655
1715
|
this._progress = clamped;
|
|
1716
|
+
this._cancelCameraAnimation();
|
|
1656
1717
|
if (this._paused)
|
|
1657
1718
|
return;
|
|
1658
1719
|
this._scheduleProgressForward(clamped);
|
|
@@ -1720,6 +1781,47 @@ class AnimationController {
|
|
|
1720
1781
|
this._assertNotDestroyed();
|
|
1721
1782
|
return this._paused;
|
|
1722
1783
|
}
|
|
1784
|
+
zoomToElement(id, options = {}) {
|
|
1785
|
+
this._assertNotDestroyed();
|
|
1786
|
+
const svg = this._getSceneSvg();
|
|
1787
|
+
if (!this._bundle || !svg) {
|
|
1788
|
+
throw new ControllerError("CAMERA_NOT_INITIALIZED", "zoomToElement() requires an initialized scene SVG");
|
|
1789
|
+
}
|
|
1790
|
+
const elementBounds = getCurrentElementBounds(svg, id);
|
|
1791
|
+
if (!elementBounds) {
|
|
1792
|
+
throw new ControllerError("CAMERA_TARGET_NOT_FOUND", `Camera target element "${id}" was not found`, {
|
|
1793
|
+
elementId: id,
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
this._applyCameraDestination(expandViewBox(elementBounds, this._resolveCameraPadding(options)), { type: "element", id }, options);
|
|
1797
|
+
}
|
|
1798
|
+
zoomToArea(area, options = {}) {
|
|
1799
|
+
this._assertNotDestroyed();
|
|
1800
|
+
if (!this._bundle || !this._getSceneSvg()) {
|
|
1801
|
+
throw new ControllerError("CAMERA_NOT_INITIALIZED", "zoomToArea() requires an initialized scene SVG");
|
|
1802
|
+
}
|
|
1803
|
+
this._assertValidCameraArea(area);
|
|
1804
|
+
const bounds = getGridAreaBounds(this._bundle, area);
|
|
1805
|
+
this._applyCameraDestination(expandViewBox(bounds, this._resolveCameraPadding(options)), {
|
|
1806
|
+
type: "area",
|
|
1807
|
+
at: [...area.at],
|
|
1808
|
+
size: [...area.size],
|
|
1809
|
+
}, options);
|
|
1810
|
+
}
|
|
1811
|
+
resetZoom(options = {}) {
|
|
1812
|
+
this._assertNotDestroyed();
|
|
1813
|
+
if (!this._bundle || !this._getSceneSvg()) {
|
|
1814
|
+
throw new ControllerError("CAMERA_NOT_INITIALIZED", "resetZoom() requires an initialized scene SVG");
|
|
1815
|
+
}
|
|
1816
|
+
this._applyCameraDestination(getResolvedViewBox(this._bundle), { type: "reset" }, options);
|
|
1817
|
+
}
|
|
1818
|
+
getCameraState() {
|
|
1819
|
+
this._assertNotDestroyed();
|
|
1820
|
+
if (!this._cameraState) {
|
|
1821
|
+
throw new ControllerError("CAMERA_NOT_INITIALIZED", "Camera state is not initialized");
|
|
1822
|
+
}
|
|
1823
|
+
return copyCameraState(this._cameraState);
|
|
1824
|
+
}
|
|
1723
1825
|
/**
|
|
1724
1826
|
* Destroy controller and clean up all listeners and resources.
|
|
1725
1827
|
*/
|
|
@@ -1728,12 +1830,14 @@ class AnimationController {
|
|
|
1728
1830
|
this._unbindScroll();
|
|
1729
1831
|
this._cancelFrame();
|
|
1730
1832
|
this._cancelTransition();
|
|
1833
|
+
this._cancelCameraAnimation();
|
|
1731
1834
|
if (this._ownsEngine)
|
|
1732
1835
|
this._engine.destroy();
|
|
1733
1836
|
this._listeners.clear();
|
|
1734
1837
|
this._bundle = null;
|
|
1735
1838
|
this._container = null;
|
|
1736
1839
|
this._pendingProgress = null;
|
|
1840
|
+
this._cameraState = null;
|
|
1737
1841
|
this._destroyed = true;
|
|
1738
1842
|
}
|
|
1739
1843
|
// ── Event system ───────────────────────────────────────────────────────
|
|
@@ -1823,6 +1927,121 @@ class AnimationController {
|
|
|
1823
1927
|
updateElementTransforms(svg, updates, connectors);
|
|
1824
1928
|
this._applyLifecycleChanges(updates);
|
|
1825
1929
|
this._applyConnectorLifecycleChanges(connectors);
|
|
1930
|
+
this._applyCameraForProgress(this._progress);
|
|
1931
|
+
}
|
|
1932
|
+
_applyCameraForProgress(progress) {
|
|
1933
|
+
const svg = this._getSceneSvg();
|
|
1934
|
+
if (!this._bundle || !svg)
|
|
1935
|
+
return;
|
|
1936
|
+
const sceneStops = this.scenes;
|
|
1937
|
+
if (sceneStops.length === 0)
|
|
1938
|
+
return;
|
|
1939
|
+
const { previous, next } = surroundingStops(sceneStops, progress);
|
|
1940
|
+
const previousCamera = this._effectiveCameraAt(previous.index);
|
|
1941
|
+
const nextCamera = this._effectiveCameraAt(next.index);
|
|
1942
|
+
const previousViewBox = this._resolveCameraViewBox(previousCamera);
|
|
1943
|
+
const nextViewBox = this._resolveCameraViewBox(nextCamera);
|
|
1944
|
+
const range = next.scene.progress - previous.scene.progress;
|
|
1945
|
+
const rawT = range <= 0 ? 0 : (progress - previous.scene.progress) / range;
|
|
1946
|
+
const t = resolveCameraEasing(nextCamera.easing)(Math.max(0, Math.min(1, rawT)));
|
|
1947
|
+
const viewBox = lerpViewBox(previousViewBox, nextViewBox, t);
|
|
1948
|
+
const target = t < 1 ? previousCamera.target : nextCamera.target;
|
|
1949
|
+
this._setCameraViewBox(svg, viewBox, target);
|
|
1950
|
+
}
|
|
1951
|
+
_effectiveCameraAt(index) {
|
|
1952
|
+
for (let i = index; i >= 0; i--) {
|
|
1953
|
+
const camera = this.scenes[i]?.camera;
|
|
1954
|
+
if (camera) {
|
|
1955
|
+
return {
|
|
1956
|
+
target: camera.target,
|
|
1957
|
+
padding: camera.padding,
|
|
1958
|
+
duration: camera.duration,
|
|
1959
|
+
easing: camera.easing,
|
|
1960
|
+
};
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
return { target: { type: "reset" } };
|
|
1964
|
+
}
|
|
1965
|
+
_resolveCameraViewBox(camera) {
|
|
1966
|
+
if (!this._bundle)
|
|
1967
|
+
throw new ControllerError("CAMERA_NOT_INITIALIZED", "Camera requires an initialized bundle");
|
|
1968
|
+
if (camera.target.type === "reset")
|
|
1969
|
+
return getResolvedViewBox(this._bundle);
|
|
1970
|
+
if (camera.target.type === "area") {
|
|
1971
|
+
return expandViewBox(getGridAreaBounds(this._bundle, { at: camera.target.at, size: camera.target.size }), camera.padding ?? 32);
|
|
1972
|
+
}
|
|
1973
|
+
const svg = this._getSceneSvg();
|
|
1974
|
+
if (!svg)
|
|
1975
|
+
throw new ControllerError("CAMERA_NOT_INITIALIZED", "Camera requires an initialized scene SVG");
|
|
1976
|
+
const bounds = getCurrentElementBounds(svg, camera.target.id);
|
|
1977
|
+
if (!bounds) {
|
|
1978
|
+
throw new ControllerError("CAMERA_TARGET_NOT_FOUND", `Camera target element "${camera.target.id}" was not found`, {
|
|
1979
|
+
elementId: camera.target.id,
|
|
1980
|
+
});
|
|
1981
|
+
}
|
|
1982
|
+
return expandViewBox(bounds, camera.padding ?? 32);
|
|
1983
|
+
}
|
|
1984
|
+
_applyCameraDestination(viewBox, target, options) {
|
|
1985
|
+
const svg = this._getSceneSvg();
|
|
1986
|
+
if (!svg)
|
|
1987
|
+
throw new ControllerError("CAMERA_NOT_INITIALIZED", "Camera requires an initialized scene SVG");
|
|
1988
|
+
this._cancelCameraAnimation();
|
|
1989
|
+
const duration = this._resolveCameraDuration(options);
|
|
1990
|
+
if (duration === 0) {
|
|
1991
|
+
this._setCameraViewBox(svg, viewBox, target);
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1994
|
+
const from = currentSvgViewBox(svg) ?? this._cameraState?.viewBox ?? viewBox;
|
|
1995
|
+
const easing = resolveCameraEasing(options.easing ?? this._config.transitionEasing);
|
|
1996
|
+
const start = performance.now();
|
|
1997
|
+
const step = (now) => {
|
|
1998
|
+
const t = Math.min((now - start) / duration, 1);
|
|
1999
|
+
this._setCameraViewBox(svg, lerpViewBox(from, viewBox, easing(t)), target);
|
|
2000
|
+
if (t < 1) {
|
|
2001
|
+
this._cameraAnim = requestAnimationFrame(step);
|
|
2002
|
+
}
|
|
2003
|
+
else {
|
|
2004
|
+
this._cameraAnim = null;
|
|
2005
|
+
}
|
|
2006
|
+
};
|
|
2007
|
+
this._cameraAnim = requestAnimationFrame(step);
|
|
2008
|
+
}
|
|
2009
|
+
_setCameraViewBox(svg, viewBox, target) {
|
|
2010
|
+
applySceneViewBox(svg, viewBox);
|
|
2011
|
+
const full = this._bundle ? getResolvedViewBox(this._bundle) : viewBox;
|
|
2012
|
+
this._cameraState = {
|
|
2013
|
+
viewBox,
|
|
2014
|
+
target,
|
|
2015
|
+
isZoomed: !sameViewBox(viewBox, full),
|
|
2016
|
+
};
|
|
2017
|
+
this._emit("camera-change", this.getCameraState());
|
|
2018
|
+
}
|
|
2019
|
+
_getSceneSvg() {
|
|
2020
|
+
return (this._sceneElement ?? this._container?.querySelector("svg") ?? null);
|
|
2021
|
+
}
|
|
2022
|
+
_resolveCameraPadding(options) {
|
|
2023
|
+
const padding = options.padding ?? 32;
|
|
2024
|
+
if (!Number.isFinite(padding) || padding < 0 || padding > 2048) {
|
|
2025
|
+
throw new ControllerError("INVALID_CAMERA_OPTIONS", "Camera padding must be between 0 and 2048");
|
|
2026
|
+
}
|
|
2027
|
+
return padding;
|
|
2028
|
+
}
|
|
2029
|
+
_resolveCameraDuration(options) {
|
|
2030
|
+
const duration = options.duration ?? this._config.transitionDuration;
|
|
2031
|
+
if (!Number.isInteger(duration) || duration < 0 || duration > 10000) {
|
|
2032
|
+
throw new ControllerError("INVALID_CAMERA_OPTIONS", "Camera duration must be an integer from 0 to 10000");
|
|
2033
|
+
}
|
|
2034
|
+
return duration;
|
|
2035
|
+
}
|
|
2036
|
+
_assertValidCameraArea(area) {
|
|
2037
|
+
if (!Array.isArray(area.at) ||
|
|
2038
|
+
area.at.length !== 2 ||
|
|
2039
|
+
!area.at.every((value) => Number.isFinite(value) && value >= 0) ||
|
|
2040
|
+
!Array.isArray(area.size) ||
|
|
2041
|
+
area.size.length !== 2 ||
|
|
2042
|
+
!area.size.every((value) => Number.isFinite(value) && value > 0)) {
|
|
2043
|
+
throw new ControllerError("INVALID_CAMERA_OPTIONS", "Camera area must use non-negative at and positive size");
|
|
2044
|
+
}
|
|
1826
2045
|
}
|
|
1827
2046
|
_applyLifecycleChanges(elements) {
|
|
1828
2047
|
for (const elDef of elements) {
|
|
@@ -2002,6 +2221,12 @@ class AnimationController {
|
|
|
2002
2221
|
this._transitionAnim = null;
|
|
2003
2222
|
}
|
|
2004
2223
|
}
|
|
2224
|
+
_cancelCameraAnimation() {
|
|
2225
|
+
if (this._cameraAnim !== null) {
|
|
2226
|
+
cancelAnimationFrame(this._cameraAnim);
|
|
2227
|
+
this._cameraAnim = null;
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2005
2230
|
// ── Scroll binding ─────────────────────────────────────────────────────
|
|
2006
2231
|
_bindScroll() {
|
|
2007
2232
|
const container = this._config.container;
|
|
@@ -2186,9 +2411,70 @@ function oppositeEntryAnimation(exit) {
|
|
|
2186
2411
|
return "none";
|
|
2187
2412
|
}
|
|
2188
2413
|
}
|
|
2414
|
+
function surroundingStops(scenes, progress) {
|
|
2415
|
+
let previousIndex = 0;
|
|
2416
|
+
let nextIndex = scenes.length - 1;
|
|
2417
|
+
for (let i = 0; i < scenes.length; i++) {
|
|
2418
|
+
if (scenes[i].progress <= progress)
|
|
2419
|
+
previousIndex = i;
|
|
2420
|
+
if (scenes[i].progress >= progress) {
|
|
2421
|
+
nextIndex = i;
|
|
2422
|
+
break;
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
return {
|
|
2426
|
+
previous: { scene: scenes[previousIndex], index: previousIndex },
|
|
2427
|
+
next: { scene: scenes[nextIndex], index: nextIndex },
|
|
2428
|
+
};
|
|
2429
|
+
}
|
|
2430
|
+
function expandViewBox(viewBox, padding) {
|
|
2431
|
+
return {
|
|
2432
|
+
minX: roundCameraNumber(viewBox.minX - padding),
|
|
2433
|
+
minY: roundCameraNumber(viewBox.minY - padding),
|
|
2434
|
+
width: roundCameraNumber(Math.max(1, viewBox.width + padding * 2)),
|
|
2435
|
+
height: roundCameraNumber(Math.max(1, viewBox.height + padding * 2)),
|
|
2436
|
+
};
|
|
2437
|
+
}
|
|
2438
|
+
function lerpViewBox(from, to, t) {
|
|
2439
|
+
return {
|
|
2440
|
+
minX: roundCameraNumber(from.minX + (to.minX - from.minX) * t),
|
|
2441
|
+
minY: roundCameraNumber(from.minY + (to.minY - from.minY) * t),
|
|
2442
|
+
width: roundCameraNumber(from.width + (to.width - from.width) * t),
|
|
2443
|
+
height: roundCameraNumber(from.height + (to.height - from.height) * t),
|
|
2444
|
+
};
|
|
2445
|
+
}
|
|
2446
|
+
function sameViewBox(a, b) {
|
|
2447
|
+
return a.minX === b.minX && a.minY === b.minY && a.width === b.width && a.height === b.height;
|
|
2448
|
+
}
|
|
2449
|
+
function currentSvgViewBox(svg) {
|
|
2450
|
+
const raw = svg.getAttribute("viewBox");
|
|
2451
|
+
if (!raw)
|
|
2452
|
+
return undefined;
|
|
2453
|
+
const parts = raw.trim().split(/\s+/).map(Number);
|
|
2454
|
+
if (parts.length !== 4 || parts.some((part) => !Number.isFinite(part)))
|
|
2455
|
+
return undefined;
|
|
2456
|
+
return { minX: parts[0], minY: parts[1], width: parts[2], height: parts[3] };
|
|
2457
|
+
}
|
|
2458
|
+
function resolveCameraEasing(easing) {
|
|
2459
|
+
return resolveEasing(easing === "ease-in-out" ? "easeInOutCubic" : easing === "ease-out" ? "easeOutCubic" : "linear");
|
|
2460
|
+
}
|
|
2461
|
+
function copyCameraState(state) {
|
|
2462
|
+
return {
|
|
2463
|
+
viewBox: { ...state.viewBox },
|
|
2464
|
+
target: state.target?.type === "area"
|
|
2465
|
+
? { type: "area", at: [...state.target.at], size: [...state.target.size] }
|
|
2466
|
+
: state.target
|
|
2467
|
+
? { ...state.target }
|
|
2468
|
+
: undefined,
|
|
2469
|
+
isZoomed: state.isZoomed,
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2472
|
+
function roundCameraNumber(value) {
|
|
2473
|
+
return Math.round(value * 1000) / 1000;
|
|
2474
|
+
}
|
|
2189
2475
|
|
|
2190
2476
|
const RUNTIME_BUNDLE_FORMAT = "isostate-runtime-bundle";
|
|
2191
|
-
const RUNTIME_VERSION = "0.
|
|
2477
|
+
const RUNTIME_VERSION = "0.2.0";
|
|
2192
2478
|
const HEX_DIGEST_PATTERN = /^[a-f0-9]{64}$/;
|
|
2193
2479
|
/** Mount a compiled runtime bundle into an HTML element. */
|
|
2194
2480
|
function mountScene(target, bundle, options = {}) {
|
|
@@ -2278,6 +2564,10 @@ function getResolvedConfig(bundle, options = {}) {
|
|
|
2278
2564
|
align: [...bundle.layout.align],
|
|
2279
2565
|
},
|
|
2280
2566
|
viewBox: getResolvedViewBox(bundle),
|
|
2567
|
+
camera: {
|
|
2568
|
+
viewBox: getResolvedViewBox(bundle),
|
|
2569
|
+
isZoomed: false,
|
|
2570
|
+
},
|
|
2281
2571
|
theme: bundle.theme,
|
|
2282
2572
|
themeVars: getResolvedThemeVars(bundle, options.themeVars),
|
|
2283
2573
|
scenes: bundle.scenes.map((scene) => ({
|
|
@@ -2388,4 +2678,4 @@ function rotr(value, bits) {
|
|
|
2388
2678
|
}
|
|
2389
2679
|
|
|
2390
2680
|
export { AnimationController as A, DEFAULT_CELL_SIZE as D, AnimationEngine as a, AssetRegistryImpl as b, applyThemeToElement as c, buildSceneDOM as d, calculateTransform as e, calculateVisualSize as f, composeTheme as g, createAssetRegistry as h, createDefaultRegistry as i, easeInCubic as j, easeInOutCubic as k, easeOutCubic as l, linear as m, mountScene as n, resolveTheme as o, projectToScreen as p, resolveEasing as r };
|
|
2391
|
-
//# sourceMappingURL=index-
|
|
2681
|
+
//# sourceMappingURL=index-DoyWxJDm.js.map
|