Mission Control Dashboard - Initial implementation
This commit is contained in:
+38
@@ -0,0 +1,38 @@
|
||||
import { observeTimeline } from 'motion-dom';
|
||||
import { canUseNativeTimeline } from './utils/can-use-native-timeline.mjs';
|
||||
import { getTimeline } from './utils/get-timeline.mjs';
|
||||
import { offsetToViewTimelineRange } from './utils/offset-to-range.mjs';
|
||||
|
||||
function attachToAnimation(animation, options) {
|
||||
const timeline = getTimeline(options);
|
||||
const range = options.target
|
||||
? offsetToViewTimelineRange(options.offset)
|
||||
: undefined;
|
||||
/**
|
||||
* Use native timeline when:
|
||||
* - No target: ScrollTimeline (existing behaviour)
|
||||
* - Target with mappable offset: ViewTimeline with named range
|
||||
* - Target with unmappable offset: fall back to JS observe
|
||||
*/
|
||||
const useNative = options.target
|
||||
? canUseNativeTimeline(options.target) && !!range
|
||||
: canUseNativeTimeline();
|
||||
return animation.attachTimeline({
|
||||
timeline: useNative ? timeline : undefined,
|
||||
...(range &&
|
||||
useNative && {
|
||||
rangeStart: range.rangeStart,
|
||||
rangeEnd: range.rangeEnd,
|
||||
}),
|
||||
observe: (valueAnimation) => {
|
||||
valueAnimation.pause();
|
||||
return observeTimeline((progress) => {
|
||||
valueAnimation.time =
|
||||
valueAnimation.iterationDuration * progress;
|
||||
}, timeline);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export { attachToAnimation };
|
||||
//# sourceMappingURL=attach-animation.mjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"attach-animation.mjs","sources":["../../../../../src/render/dom/scroll/attach-animation.ts"],"sourcesContent":["import { AnimationPlaybackControls, observeTimeline } from \"motion-dom\"\nimport { ScrollOptionsWithDefaults } from \"./types\"\nimport { canUseNativeTimeline } from \"./utils/can-use-native-timeline\"\nimport { getTimeline } from \"./utils/get-timeline\"\nimport { offsetToViewTimelineRange } from \"./utils/offset-to-range\"\n\nexport function attachToAnimation(\n animation: AnimationPlaybackControls,\n options: ScrollOptionsWithDefaults\n) {\n const timeline = getTimeline(options)\n\n const range = options.target\n ? offsetToViewTimelineRange(options.offset)\n : undefined\n\n /**\n * Use native timeline when:\n * - No target: ScrollTimeline (existing behaviour)\n * - Target with mappable offset: ViewTimeline with named range\n * - Target with unmappable offset: fall back to JS observe\n */\n const useNative = options.target\n ? canUseNativeTimeline(options.target) && !!range\n : canUseNativeTimeline()\n\n return animation.attachTimeline({\n timeline: useNative ? timeline : undefined,\n ...(range &&\n useNative && {\n rangeStart: range.rangeStart,\n rangeEnd: range.rangeEnd,\n }),\n observe: (valueAnimation) => {\n valueAnimation.pause()\n\n return observeTimeline((progress) => {\n valueAnimation.time =\n valueAnimation.iterationDuration * progress\n }, timeline)\n },\n })\n}\n"],"names":[],"mappings":";;;;;AAMM,SAAU,iBAAiB,CAC7B,SAAoC,EACpC,OAAkC,EAAA;AAElC,IAAA,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC;AAErC,IAAA,MAAM,KAAK,GAAG,OAAO,CAAC;AAClB,UAAE,yBAAyB,CAAC,OAAO,CAAC,MAAM;UACxC,SAAS;AAEf;;;;;AAKG;AACH,IAAA,MAAM,SAAS,GAAG,OAAO,CAAC;UACpB,oBAAoB,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;UAC1C,oBAAoB,EAAE;IAE5B,OAAO,SAAS,CAAC,cAAc,CAAC;QAC5B,QAAQ,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS;AAC1C,QAAA,IAAI,KAAK;AACL,YAAA,SAAS,IAAI;YACT,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ;SAC3B,CAAC;AACN,QAAA,OAAO,EAAE,CAAC,cAAc,KAAI;YACxB,cAAc,CAAC,KAAK,EAAE;AAEtB,YAAA,OAAO,eAAe,CAAC,CAAC,QAAQ,KAAI;AAChC,gBAAA,cAAc,CAAC,IAAI;AACf,oBAAA,cAAc,CAAC,iBAAiB,GAAG,QAAQ;YACnD,CAAC,EAAE,QAAQ,CAAC;QAChB,CAAC;AACJ,KAAA,CAAC;AACN;;;;"}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { observeTimeline } from 'motion-dom';
|
||||
import { scrollInfo } from './track.mjs';
|
||||
import { getTimeline } from './utils/get-timeline.mjs';
|
||||
|
||||
/**
|
||||
* If the onScroll function has two arguments, it's expecting
|
||||
* more specific information about the scroll from scrollInfo.
|
||||
*/
|
||||
function isOnScrollWithInfo(onScroll) {
|
||||
return onScroll.length === 2;
|
||||
}
|
||||
function attachToFunction(onScroll, options) {
|
||||
if (isOnScrollWithInfo(onScroll)) {
|
||||
return scrollInfo((info) => {
|
||||
onScroll(info[options.axis].progress, info);
|
||||
}, options);
|
||||
}
|
||||
else {
|
||||
return observeTimeline(onScroll, getTimeline(options));
|
||||
}
|
||||
}
|
||||
|
||||
export { attachToFunction };
|
||||
//# sourceMappingURL=attach-function.mjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"attach-function.mjs","sources":["../../../../../src/render/dom/scroll/attach-function.ts"],"sourcesContent":["import { observeTimeline } from \"motion-dom\"\nimport { scrollInfo } from \"./track\"\nimport { OnScroll, OnScrollWithInfo, ScrollOptionsWithDefaults } from \"./types\"\nimport { getTimeline } from \"./utils/get-timeline\"\n\n/**\n * If the onScroll function has two arguments, it's expecting\n * more specific information about the scroll from scrollInfo.\n */\nfunction isOnScrollWithInfo(onScroll: OnScroll): onScroll is OnScrollWithInfo {\n return onScroll.length === 2\n}\n\nexport function attachToFunction(\n onScroll: OnScroll,\n options: ScrollOptionsWithDefaults\n) {\n if (isOnScrollWithInfo(onScroll)) {\n return scrollInfo((info) => {\n onScroll(info[options.axis!].progress, info)\n }, options)\n } else {\n return observeTimeline(onScroll, getTimeline(options))\n }\n}\n"],"names":[],"mappings":";;;;AAKA;;;AAGG;AACH,SAAS,kBAAkB,CAAC,QAAkB,EAAA;AAC1C,IAAA,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC;AAChC;AAEM,SAAU,gBAAgB,CAC5B,QAAkB,EAClB,OAAkC,EAAA;AAElC,IAAA,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE;AAC9B,QAAA,OAAO,UAAU,CAAC,CAAC,IAAI,KAAI;AACvB,YAAA,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,IAAK,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC;QAChD,CAAC,EAAE,OAAO,CAAC;IACf;SAAO;QACH,OAAO,eAAe,CAAC,QAAQ,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAC1D;AACJ;;;;"}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { noop } from 'motion-utils';
|
||||
import { attachToAnimation } from './attach-animation.mjs';
|
||||
import { attachToFunction } from './attach-function.mjs';
|
||||
|
||||
function scroll(onScroll, { axis = "y", container = document.scrollingElement, ...options } = {}) {
|
||||
if (!container)
|
||||
return noop;
|
||||
const optionsWithDefaults = { axis, container, ...options };
|
||||
return typeof onScroll === "function"
|
||||
? attachToFunction(onScroll, optionsWithDefaults)
|
||||
: attachToAnimation(onScroll, optionsWithDefaults);
|
||||
}
|
||||
|
||||
export { scroll };
|
||||
//# sourceMappingURL=index.mjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.mjs","sources":["../../../../../src/render/dom/scroll/index.ts"],"sourcesContent":["import { AnimationPlaybackControls } from \"motion-dom\"\nimport { noop } from \"motion-utils\"\nimport { attachToAnimation } from \"./attach-animation\"\nimport { attachToFunction } from \"./attach-function\"\nimport { OnScroll, ScrollOptions } from \"./types\"\n\nexport function scroll(\n onScroll: OnScroll | AnimationPlaybackControls,\n {\n axis = \"y\",\n container = document.scrollingElement as Element,\n ...options\n }: ScrollOptions = {}\n): VoidFunction {\n if (!container) return noop as VoidFunction\n\n const optionsWithDefaults = { axis, container, ...options }\n\n return typeof onScroll === \"function\"\n ? attachToFunction(onScroll, optionsWithDefaults)\n : attachToAnimation(onScroll, optionsWithDefaults)\n}\n"],"names":[],"mappings":";;;;SAMgB,MAAM,CAClB,QAA8C,EAC9C,EACI,IAAI,GAAG,GAAG,EACV,SAAS,GAAG,QAAQ,CAAC,gBAA2B,EAChD,GAAG,OAAO,KACK,EAAE,EAAA;AAErB,IAAA,IAAI,CAAC,SAAS;AAAE,QAAA,OAAO,IAAoB;IAE3C,MAAM,mBAAmB,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,OAAO,EAAE;IAE3D,OAAO,OAAO,QAAQ,KAAK;AACvB,UAAE,gBAAgB,CAAC,QAAQ,EAAE,mBAAmB;AAChD,UAAE,iBAAiB,CAAC,QAAQ,EAAE,mBAAmB,CAAC;AAC1D;;;;"}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
import { progress, velocityPerSecond } from 'motion-utils';
|
||||
|
||||
/**
|
||||
* A time in milliseconds, beyond which we consider the scroll velocity to be 0.
|
||||
*/
|
||||
const maxElapsed = 50;
|
||||
const createAxisInfo = () => ({
|
||||
current: 0,
|
||||
offset: [],
|
||||
progress: 0,
|
||||
scrollLength: 0,
|
||||
targetOffset: 0,
|
||||
targetLength: 0,
|
||||
containerLength: 0,
|
||||
velocity: 0,
|
||||
});
|
||||
const createScrollInfo = () => ({
|
||||
time: 0,
|
||||
x: createAxisInfo(),
|
||||
y: createAxisInfo(),
|
||||
});
|
||||
const keys = {
|
||||
x: {
|
||||
length: "Width",
|
||||
position: "Left",
|
||||
},
|
||||
y: {
|
||||
length: "Height",
|
||||
position: "Top",
|
||||
},
|
||||
};
|
||||
function updateAxisInfo(element, axisName, info, time) {
|
||||
const axis = info[axisName];
|
||||
const { length, position } = keys[axisName];
|
||||
const prev = axis.current;
|
||||
const prevTime = info.time;
|
||||
axis.current = Math.abs(element[`scroll${position}`]);
|
||||
axis.scrollLength = element[`scroll${length}`] - element[`client${length}`];
|
||||
axis.offset.length = 0;
|
||||
axis.offset[0] = 0;
|
||||
axis.offset[1] = axis.scrollLength;
|
||||
axis.progress = progress(0, axis.scrollLength, axis.current);
|
||||
const elapsed = time - prevTime;
|
||||
axis.velocity =
|
||||
elapsed > maxElapsed
|
||||
? 0
|
||||
: velocityPerSecond(axis.current - prev, elapsed);
|
||||
}
|
||||
function updateScrollInfo(element, info, time) {
|
||||
updateAxisInfo(element, "x", info, time);
|
||||
updateAxisInfo(element, "y", info, time);
|
||||
info.time = time;
|
||||
}
|
||||
|
||||
export { createScrollInfo, updateScrollInfo };
|
||||
//# sourceMappingURL=info.mjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"info.mjs","sources":["../../../../../src/render/dom/scroll/info.ts"],"sourcesContent":["import { progress, velocityPerSecond } from \"motion-utils\"\nimport { AxisScrollInfo, ScrollInfo } from \"./types\"\n\n/**\n * A time in milliseconds, beyond which we consider the scroll velocity to be 0.\n */\nconst maxElapsed = 50\n\nconst createAxisInfo = (): AxisScrollInfo => ({\n current: 0,\n offset: [],\n progress: 0,\n scrollLength: 0,\n targetOffset: 0,\n targetLength: 0,\n containerLength: 0,\n velocity: 0,\n})\n\nexport const createScrollInfo = (): ScrollInfo => ({\n time: 0,\n x: createAxisInfo(),\n y: createAxisInfo(),\n})\n\nconst keys = {\n x: {\n length: \"Width\",\n position: \"Left\",\n },\n y: {\n length: \"Height\",\n position: \"Top\",\n },\n} as const\n\nfunction updateAxisInfo(\n element: Element,\n axisName: \"x\" | \"y\",\n info: ScrollInfo,\n time: number\n) {\n const axis = info[axisName]\n const { length, position } = keys[axisName]\n\n const prev = axis.current\n const prevTime = info.time\n\n axis.current = Math.abs(element[`scroll${position}`])\n axis.scrollLength = element[`scroll${length}`] - element[`client${length}`]\n\n axis.offset.length = 0\n axis.offset[0] = 0\n axis.offset[1] = axis.scrollLength\n axis.progress = progress(0, axis.scrollLength, axis.current)\n\n const elapsed = time - prevTime\n axis.velocity =\n elapsed > maxElapsed\n ? 0\n : velocityPerSecond(axis.current - prev, elapsed)\n}\n\nexport function updateScrollInfo(\n element: Element,\n info: ScrollInfo,\n time: number\n) {\n updateAxisInfo(element, \"x\", info, time)\n updateAxisInfo(element, \"y\", info, time)\n info.time = time\n}\n"],"names":[],"mappings":";;AAGA;;AAEG;AACH,MAAM,UAAU,GAAG,EAAE;AAErB,MAAM,cAAc,GAAG,OAAuB;AAC1C,IAAA,OAAO,EAAE,CAAC;AACV,IAAA,MAAM,EAAE,EAAE;AACV,IAAA,QAAQ,EAAE,CAAC;AACX,IAAA,YAAY,EAAE,CAAC;AACf,IAAA,YAAY,EAAE,CAAC;AACf,IAAA,YAAY,EAAE,CAAC;AACf,IAAA,eAAe,EAAE,CAAC;AAClB,IAAA,QAAQ,EAAE,CAAC;AACd,CAAA,CAAC;AAEK,MAAM,gBAAgB,GAAG,OAAmB;AAC/C,IAAA,IAAI,EAAE,CAAC;IACP,CAAC,EAAE,cAAc,EAAE;IACnB,CAAC,EAAE,cAAc,EAAE;AACtB,CAAA;AAED,MAAM,IAAI,GAAG;AACT,IAAA,CAAC,EAAE;AACC,QAAA,MAAM,EAAE,OAAO;AACf,QAAA,QAAQ,EAAE,MAAM;AACnB,KAAA;AACD,IAAA,CAAC,EAAE;AACC,QAAA,MAAM,EAAE,QAAQ;AAChB,QAAA,QAAQ,EAAE,KAAK;AAClB,KAAA;CACK;AAEV,SAAS,cAAc,CACnB,OAAgB,EAChB,QAAmB,EACnB,IAAgB,EAChB,IAAY,EAAA;AAEZ,IAAA,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC3B,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC;AAE3C,IAAA,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO;AACzB,IAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI;AAE1B,IAAA,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA,MAAA,EAAS,QAAQ,CAAA,CAAE,CAAC,CAAC;AACrD,IAAA,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,SAAS,MAAM,CAAA,CAAE,CAAC,GAAG,OAAO,CAAC,CAAA,MAAA,EAAS,MAAM,CAAA,CAAE,CAAC;AAE3E,IAAA,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;AACtB,IAAA,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC;IAClB,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,YAAY;AAClC,IAAA,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC;AAE5D,IAAA,MAAM,OAAO,GAAG,IAAI,GAAG,QAAQ;AAC/B,IAAA,IAAI,CAAC,QAAQ;AACT,QAAA,OAAO,GAAG;AACN,cAAE;cACA,iBAAiB,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,OAAO,CAAC;AAC7D;SAEgB,gBAAgB,CAC5B,OAAgB,EAChB,IAAgB,EAChB,IAAY,EAAA;IAEZ,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC;IACxC,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC;AACxC,IAAA,IAAI,CAAC,IAAI,GAAG,IAAI;AACpB;;;;"}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
const namedEdges = {
|
||||
start: 0,
|
||||
center: 0.5,
|
||||
end: 1,
|
||||
};
|
||||
function resolveEdge(edge, length, inset = 0) {
|
||||
let delta = 0;
|
||||
/**
|
||||
* If we have this edge defined as a preset, replace the definition
|
||||
* with the numerical value.
|
||||
*/
|
||||
if (edge in namedEdges) {
|
||||
edge = namedEdges[edge];
|
||||
}
|
||||
/**
|
||||
* Handle unit values
|
||||
*/
|
||||
if (typeof edge === "string") {
|
||||
const asNumber = parseFloat(edge);
|
||||
if (edge.endsWith("px")) {
|
||||
delta = asNumber;
|
||||
}
|
||||
else if (edge.endsWith("%")) {
|
||||
edge = asNumber / 100;
|
||||
}
|
||||
else if (edge.endsWith("vw")) {
|
||||
delta = (asNumber / 100) * document.documentElement.clientWidth;
|
||||
}
|
||||
else if (edge.endsWith("vh")) {
|
||||
delta = (asNumber / 100) * document.documentElement.clientHeight;
|
||||
}
|
||||
else {
|
||||
edge = asNumber;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* If the edge is defined as a number, handle as a progress value.
|
||||
*/
|
||||
if (typeof edge === "number") {
|
||||
delta = length * edge;
|
||||
}
|
||||
return inset + delta;
|
||||
}
|
||||
|
||||
export { namedEdges, resolveEdge };
|
||||
//# sourceMappingURL=edge.mjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"edge.mjs","sources":["../../../../../../src/render/dom/scroll/offsets/edge.ts"],"sourcesContent":["import { Edge, NamedEdges } from \"../types\"\n\nexport const namedEdges: Record<NamedEdges, number> = {\n start: 0,\n center: 0.5,\n end: 1,\n}\n\nexport function resolveEdge(edge: Edge, length: number, inset = 0) {\n let delta = 0\n\n /**\n * If we have this edge defined as a preset, replace the definition\n * with the numerical value.\n */\n if (edge in namedEdges) {\n edge = namedEdges[edge as NamedEdges]\n }\n\n /**\n * Handle unit values\n */\n if (typeof edge === \"string\") {\n const asNumber = parseFloat(edge)\n\n if (edge.endsWith(\"px\")) {\n delta = asNumber\n } else if (edge.endsWith(\"%\")) {\n edge = asNumber / 100\n } else if (edge.endsWith(\"vw\")) {\n delta = (asNumber / 100) * document.documentElement.clientWidth\n } else if (edge.endsWith(\"vh\")) {\n delta = (asNumber / 100) * document.documentElement.clientHeight\n } else {\n edge = asNumber\n }\n }\n\n /**\n * If the edge is defined as a number, handle as a progress value.\n */\n if (typeof edge === \"number\") {\n delta = length * edge\n }\n\n return inset + delta\n}\n"],"names":[],"mappings":"AAEO,MAAM,UAAU,GAA+B;AAClD,IAAA,KAAK,EAAE,CAAC;AACR,IAAA,MAAM,EAAE,GAAG;AACX,IAAA,GAAG,EAAE,CAAC;;AAGJ,SAAU,WAAW,CAAC,IAAU,EAAE,MAAc,EAAE,KAAK,GAAG,CAAC,EAAA;IAC7D,IAAI,KAAK,GAAG,CAAC;AAEb;;;AAGG;AACH,IAAA,IAAI,IAAI,IAAI,UAAU,EAAE;AACpB,QAAA,IAAI,GAAG,UAAU,CAAC,IAAkB,CAAC;IACzC;AAEA;;AAEG;AACH,IAAA,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;AAC1B,QAAA,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC;AAEjC,QAAA,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;YACrB,KAAK,GAAG,QAAQ;QACpB;AAAO,aAAA,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;AAC3B,YAAA,IAAI,GAAG,QAAQ,GAAG,GAAG;QACzB;AAAO,aAAA,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;AAC5B,YAAA,KAAK,GAAG,CAAC,QAAQ,GAAG,GAAG,IAAI,QAAQ,CAAC,eAAe,CAAC,WAAW;QACnE;AAAO,aAAA,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;AAC5B,YAAA,KAAK,GAAG,CAAC,QAAQ,GAAG,GAAG,IAAI,QAAQ,CAAC,eAAe,CAAC,YAAY;QACpE;aAAO;YACH,IAAI,GAAG,QAAQ;QACnB;IACJ;AAEA;;AAEG;AACH,IAAA,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;AAC1B,QAAA,KAAK,GAAG,MAAM,GAAG,IAAI;IACzB;IAEA,OAAO,KAAK,GAAG,KAAK;AACxB;;;;"}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
import { interpolate, defaultOffset } from 'motion-dom';
|
||||
import { clamp } from 'motion-utils';
|
||||
import { calcInset } from './inset.mjs';
|
||||
import { resolveOffset } from './offset.mjs';
|
||||
import { ScrollOffset } from './presets.mjs';
|
||||
|
||||
const point = { x: 0, y: 0 };
|
||||
function getTargetSize(target) {
|
||||
return "getBBox" in target && target.tagName !== "svg"
|
||||
? target.getBBox()
|
||||
: { width: target.clientWidth, height: target.clientHeight };
|
||||
}
|
||||
function resolveOffsets(container, info, options) {
|
||||
const { offset: offsetDefinition = ScrollOffset.All } = options;
|
||||
const { target = container, axis = "y" } = options;
|
||||
const lengthLabel = axis === "y" ? "height" : "width";
|
||||
const inset = target !== container ? calcInset(target, container) : point;
|
||||
/**
|
||||
* Measure the target and container. If they're the same thing then we
|
||||
* use the container's scrollWidth/Height as the target, from there
|
||||
* all other calculations can remain the same.
|
||||
*/
|
||||
const targetSize = target === container
|
||||
? { width: container.scrollWidth, height: container.scrollHeight }
|
||||
: getTargetSize(target);
|
||||
const containerSize = {
|
||||
width: container.clientWidth,
|
||||
height: container.clientHeight,
|
||||
};
|
||||
/**
|
||||
* Reset the length of the resolved offset array rather than creating a new one.
|
||||
* TODO: More reusable data structures for targetSize/containerSize would also be good.
|
||||
*/
|
||||
info[axis].offset.length = 0;
|
||||
/**
|
||||
* Populate the offset array by resolving the user's offset definition into
|
||||
* a list of pixel scroll offets.
|
||||
*/
|
||||
let hasChanged = !info[axis].interpolate;
|
||||
const numOffsets = offsetDefinition.length;
|
||||
for (let i = 0; i < numOffsets; i++) {
|
||||
const offset = resolveOffset(offsetDefinition[i], containerSize[lengthLabel], targetSize[lengthLabel], inset[axis]);
|
||||
if (!hasChanged && offset !== info[axis].interpolatorOffsets[i]) {
|
||||
hasChanged = true;
|
||||
}
|
||||
info[axis].offset[i] = offset;
|
||||
}
|
||||
/**
|
||||
* If the pixel scroll offsets have changed, create a new interpolator function
|
||||
* to map scroll value into a progress.
|
||||
*/
|
||||
if (hasChanged) {
|
||||
info[axis].interpolate = interpolate(info[axis].offset, defaultOffset(offsetDefinition), { clamp: false });
|
||||
info[axis].interpolatorOffsets = [...info[axis].offset];
|
||||
}
|
||||
info[axis].progress = clamp(0, 1, info[axis].interpolate(info[axis].current));
|
||||
}
|
||||
|
||||
export { resolveOffsets };
|
||||
//# sourceMappingURL=index.mjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.mjs","sources":["../../../../../../src/render/dom/scroll/offsets/index.ts"],"sourcesContent":["import { defaultOffset, interpolate } from \"motion-dom\"\nimport { clamp } from \"motion-utils\"\nimport { ScrollInfo, ScrollInfoOptions } from \"../types\"\nimport { calcInset } from \"./inset\"\nimport { resolveOffset } from \"./offset\"\nimport { ScrollOffset } from \"./presets\"\n\nconst point = { x: 0, y: 0 }\n\nfunction getTargetSize(target: Element) {\n return \"getBBox\" in target && target.tagName !== \"svg\"\n ? (target as SVGGraphicsElement).getBBox()\n : { width: target.clientWidth, height: target.clientHeight }\n}\n\nexport function resolveOffsets(\n container: Element,\n info: ScrollInfo,\n options: ScrollInfoOptions\n) {\n const { offset: offsetDefinition = ScrollOffset.All } = options\n const { target = container, axis = \"y\" } = options\n const lengthLabel = axis === \"y\" ? \"height\" : \"width\"\n\n const inset = target !== container ? calcInset(target, container) : point\n\n /**\n * Measure the target and container. If they're the same thing then we\n * use the container's scrollWidth/Height as the target, from there\n * all other calculations can remain the same.\n */\n const targetSize =\n target === container\n ? { width: container.scrollWidth, height: container.scrollHeight }\n : getTargetSize(target)\n\n const containerSize = {\n width: container.clientWidth,\n height: container.clientHeight,\n }\n\n /**\n * Reset the length of the resolved offset array rather than creating a new one.\n * TODO: More reusable data structures for targetSize/containerSize would also be good.\n */\n info[axis].offset.length = 0\n\n /**\n * Populate the offset array by resolving the user's offset definition into\n * a list of pixel scroll offets.\n */\n let hasChanged = !info[axis].interpolate\n\n const numOffsets = offsetDefinition.length\n for (let i = 0; i < numOffsets; i++) {\n const offset = resolveOffset(\n offsetDefinition[i],\n containerSize[lengthLabel],\n targetSize[lengthLabel],\n inset[axis]\n )\n\n if (!hasChanged && offset !== info[axis].interpolatorOffsets![i]) {\n hasChanged = true\n }\n\n info[axis].offset[i] = offset\n }\n\n /**\n * If the pixel scroll offsets have changed, create a new interpolator function\n * to map scroll value into a progress.\n */\n if (hasChanged) {\n info[axis].interpolate = interpolate(\n info[axis].offset,\n defaultOffset(offsetDefinition),\n { clamp: false }\n )\n\n info[axis].interpolatorOffsets = [...info[axis].offset]\n }\n\n info[axis].progress = clamp(\n 0,\n 1,\n info[axis].interpolate!(info[axis].current)\n )\n}\n"],"names":[],"mappings":";;;;;;AAOA,MAAM,KAAK,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AAE5B,SAAS,aAAa,CAAC,MAAe,EAAA;IAClC,OAAO,SAAS,IAAI,MAAM,IAAI,MAAM,CAAC,OAAO,KAAK;AAC7C,UAAG,MAA6B,CAAC,OAAO;AACxC,UAAE,EAAE,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE;AACpE;SAEgB,cAAc,CAC1B,SAAkB,EAClB,IAAgB,EAChB,OAA0B,EAAA;IAE1B,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,YAAY,CAAC,GAAG,EAAE,GAAG,OAAO;IAC/D,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,GAAG,GAAG,EAAE,GAAG,OAAO;AAClD,IAAA,MAAM,WAAW,GAAG,IAAI,KAAK,GAAG,GAAG,QAAQ,GAAG,OAAO;AAErD,IAAA,MAAM,KAAK,GAAG,MAAM,KAAK,SAAS,GAAG,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,KAAK;AAEzE;;;;AAIG;AACH,IAAA,MAAM,UAAU,GACZ,MAAM,KAAK;AACP,UAAE,EAAE,KAAK,EAAE,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,YAAY;AAChE,UAAE,aAAa,CAAC,MAAM,CAAC;AAE/B,IAAA,MAAM,aAAa,GAAG;QAClB,KAAK,EAAE,SAAS,CAAC,WAAW;QAC5B,MAAM,EAAE,SAAS,CAAC,YAAY;KACjC;AAED;;;AAGG;IACH,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;AAE5B;;;AAGG;IACH,IAAI,UAAU,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,WAAW;AAExC,IAAA,MAAM,UAAU,GAAG,gBAAgB,CAAC,MAAM;AAC1C,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE,EAAE;QACjC,MAAM,MAAM,GAAG,aAAa,CACxB,gBAAgB,CAAC,CAAC,CAAC,EACnB,aAAa,CAAC,WAAW,CAAC,EAC1B,UAAU,CAAC,WAAW,CAAC,EACvB,KAAK,CAAC,IAAI,CAAC,CACd;AAED,QAAA,IAAI,CAAC,UAAU,IAAI,MAAM,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC,mBAAoB,CAAC,CAAC,CAAC,EAAE;YAC9D,UAAU,GAAG,IAAI;QACrB;QAEA,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM;IACjC;AAEA;;;AAGG;IACH,IAAI,UAAU,EAAE;QACZ,IAAI,CAAC,IAAI,CAAC,CAAC,WAAW,GAAG,WAAW,CAChC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,EACjB,aAAa,CAAC,gBAAgB,CAAC,EAC/B,EAAE,KAAK,EAAE,KAAK,EAAE,CACnB;AAED,QAAA,IAAI,CAAC,IAAI,CAAC,CAAC,mBAAmB,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;IAC3D;IAEA,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,GAAG,KAAK,CACvB,CAAC,EACD,CAAC,EACD,IAAI,CAAC,IAAI,CAAC,CAAC,WAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAC9C;AACL;;;;"}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import { isHTMLElement } from 'motion-dom';
|
||||
|
||||
function calcInset(element, container) {
|
||||
const inset = { x: 0, y: 0 };
|
||||
let current = element;
|
||||
while (current && current !== container) {
|
||||
if (isHTMLElement(current)) {
|
||||
inset.x += current.offsetLeft;
|
||||
inset.y += current.offsetTop;
|
||||
current = current.offsetParent;
|
||||
}
|
||||
else if (current.tagName === "svg") {
|
||||
/**
|
||||
* This isn't an ideal approach to measuring the offset of <svg /> tags.
|
||||
* It would be preferable, given they behave like HTMLElements in most ways
|
||||
* to use offsetLeft/Top. But these don't exist on <svg />. Likewise we
|
||||
* can't use .getBBox() like most SVG elements as these provide the offset
|
||||
* relative to the SVG itself, which for <svg /> is usually 0x0.
|
||||
*/
|
||||
const svgBoundingBox = current.getBoundingClientRect();
|
||||
current = current.parentElement;
|
||||
const parentBoundingBox = current.getBoundingClientRect();
|
||||
inset.x += svgBoundingBox.left - parentBoundingBox.left;
|
||||
inset.y += svgBoundingBox.top - parentBoundingBox.top;
|
||||
}
|
||||
else if (current instanceof SVGGraphicsElement) {
|
||||
const { x, y } = current.getBBox();
|
||||
inset.x += x;
|
||||
inset.y += y;
|
||||
let svg = null;
|
||||
let parent = current.parentNode;
|
||||
while (!svg) {
|
||||
if (parent.tagName === "svg") {
|
||||
svg = parent;
|
||||
}
|
||||
parent = current.parentNode;
|
||||
}
|
||||
current = svg;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return inset;
|
||||
}
|
||||
|
||||
export { calcInset };
|
||||
//# sourceMappingURL=inset.mjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"inset.mjs","sources":["../../../../../../src/render/dom/scroll/offsets/inset.ts"],"sourcesContent":["import { isHTMLElement } from \"motion-dom\"\n\nexport function calcInset(element: Element, container: Element) {\n const inset = { x: 0, y: 0 }\n\n let current: Element | null = element\n while (current && current !== container) {\n if (isHTMLElement(current)) {\n inset.x += current.offsetLeft\n inset.y += current.offsetTop\n current = current.offsetParent\n } else if (current.tagName === \"svg\") {\n /**\n * This isn't an ideal approach to measuring the offset of <svg /> tags.\n * It would be preferable, given they behave like HTMLElements in most ways\n * to use offsetLeft/Top. But these don't exist on <svg />. Likewise we\n * can't use .getBBox() like most SVG elements as these provide the offset\n * relative to the SVG itself, which for <svg /> is usually 0x0.\n */\n const svgBoundingBox = current.getBoundingClientRect()\n current = current.parentElement!\n const parentBoundingBox = current.getBoundingClientRect()\n inset.x += svgBoundingBox.left - parentBoundingBox.left\n inset.y += svgBoundingBox.top - parentBoundingBox.top\n } else if (current instanceof SVGGraphicsElement) {\n const { x, y } = current.getBBox()\n inset.x += x\n inset.y += y\n\n let svg: SVGElement | null = null\n let parent: SVGElement = current.parentNode as SVGElement\n while (!svg) {\n if (parent.tagName === \"svg\") {\n svg = parent\n }\n parent = current.parentNode as SVGElement\n }\n current = svg\n } else {\n break\n }\n }\n\n return inset\n}\n"],"names":[],"mappings":";;AAEM,SAAU,SAAS,CAAC,OAAgB,EAAE,SAAkB,EAAA;IAC1D,MAAM,KAAK,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;IAE5B,IAAI,OAAO,GAAmB,OAAO;AACrC,IAAA,OAAO,OAAO,IAAI,OAAO,KAAK,SAAS,EAAE;AACrC,QAAA,IAAI,aAAa,CAAC,OAAO,CAAC,EAAE;AACxB,YAAA,KAAK,CAAC,CAAC,IAAI,OAAO,CAAC,UAAU;AAC7B,YAAA,KAAK,CAAC,CAAC,IAAI,OAAO,CAAC,SAAS;AAC5B,YAAA,OAAO,GAAG,OAAO,CAAC,YAAY;QAClC;AAAO,aAAA,IAAI,OAAO,CAAC,OAAO,KAAK,KAAK,EAAE;AAClC;;;;;;AAMG;AACH,YAAA,MAAM,cAAc,GAAG,OAAO,CAAC,qBAAqB,EAAE;AACtD,YAAA,OAAO,GAAG,OAAO,CAAC,aAAc;AAChC,YAAA,MAAM,iBAAiB,GAAG,OAAO,CAAC,qBAAqB,EAAE;YACzD,KAAK,CAAC,CAAC,IAAI,cAAc,CAAC,IAAI,GAAG,iBAAiB,CAAC,IAAI;YACvD,KAAK,CAAC,CAAC,IAAI,cAAc,CAAC,GAAG,GAAG,iBAAiB,CAAC,GAAG;QACzD;AAAO,aAAA,IAAI,OAAO,YAAY,kBAAkB,EAAE;YAC9C,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE;AAClC,YAAA,KAAK,CAAC,CAAC,IAAI,CAAC;AACZ,YAAA,KAAK,CAAC,CAAC,IAAI,CAAC;YAEZ,IAAI,GAAG,GAAsB,IAAI;AACjC,YAAA,IAAI,MAAM,GAAe,OAAO,CAAC,UAAwB;YACzD,OAAO,CAAC,GAAG,EAAE;AACT,gBAAA,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,EAAE;oBAC1B,GAAG,GAAG,MAAM;gBAChB;AACA,gBAAA,MAAM,GAAG,OAAO,CAAC,UAAwB;YAC7C;YACA,OAAO,GAAG,GAAG;QACjB;aAAO;YACH;QACJ;IACJ;AAEA,IAAA,OAAO,KAAK;AAChB;;;;"}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
import { resolveEdge, namedEdges } from './edge.mjs';
|
||||
|
||||
const defaultOffset = [0, 0];
|
||||
function resolveOffset(offset, containerLength, targetLength, targetInset) {
|
||||
let offsetDefinition = Array.isArray(offset) ? offset : defaultOffset;
|
||||
let targetPoint = 0;
|
||||
let containerPoint = 0;
|
||||
if (typeof offset === "number") {
|
||||
/**
|
||||
* If we're provided offset: [0, 0.5, 1] then each number x should become
|
||||
* [x, x], so we default to the behaviour of mapping 0 => 0 of both target
|
||||
* and container etc.
|
||||
*/
|
||||
offsetDefinition = [offset, offset];
|
||||
}
|
||||
else if (typeof offset === "string") {
|
||||
offset = offset.trim();
|
||||
if (offset.includes(" ")) {
|
||||
offsetDefinition = offset.split(" ");
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* If we're provided a definition like "100px" then we want to apply
|
||||
* that only to the top of the target point, leaving the container at 0.
|
||||
* Whereas a named offset like "end" should be applied to both.
|
||||
*/
|
||||
offsetDefinition = [offset, namedEdges[offset] ? offset : `0`];
|
||||
}
|
||||
}
|
||||
targetPoint = resolveEdge(offsetDefinition[0], targetLength, targetInset);
|
||||
containerPoint = resolveEdge(offsetDefinition[1], containerLength);
|
||||
return targetPoint - containerPoint;
|
||||
}
|
||||
|
||||
export { resolveOffset };
|
||||
//# sourceMappingURL=offset.mjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"offset.mjs","sources":["../../../../../../src/render/dom/scroll/offsets/offset.ts"],"sourcesContent":["import { Edge, EdgeString, Intersection, ProgressIntersection } from \"../types\"\nimport { namedEdges, resolveEdge } from \"./edge\"\n\nconst defaultOffset: ProgressIntersection = [0, 0]\n\nexport function resolveOffset(\n offset: Edge | Intersection | ProgressIntersection,\n containerLength: number,\n targetLength: number,\n targetInset: number\n) {\n let offsetDefinition: ProgressIntersection | [EdgeString, EdgeString] =\n Array.isArray(offset) ? offset : defaultOffset\n\n let targetPoint = 0\n let containerPoint = 0\n\n if (typeof offset === \"number\") {\n /**\n * If we're provided offset: [0, 0.5, 1] then each number x should become\n * [x, x], so we default to the behaviour of mapping 0 => 0 of both target\n * and container etc.\n */\n offsetDefinition = [offset, offset]\n } else if (typeof offset === \"string\") {\n offset = offset.trim() as EdgeString\n\n if (offset.includes(\" \")) {\n offsetDefinition = offset.split(\" \") as [EdgeString, EdgeString]\n } else {\n /**\n * If we're provided a definition like \"100px\" then we want to apply\n * that only to the top of the target point, leaving the container at 0.\n * Whereas a named offset like \"end\" should be applied to both.\n */\n offsetDefinition = [offset, namedEdges[offset as keyof typeof namedEdges] ? offset : `0`]\n }\n }\n\n targetPoint = resolveEdge(offsetDefinition[0], targetLength, targetInset)\n containerPoint = resolveEdge(offsetDefinition[1], containerLength)\n\n return targetPoint - containerPoint\n}\n"],"names":[],"mappings":";;AAGA,MAAM,aAAa,GAAyB,CAAC,CAAC,EAAE,CAAC,CAAC;AAE5C,SAAU,aAAa,CACzB,MAAkD,EAClD,eAAuB,EACvB,YAAoB,EACpB,WAAmB,EAAA;AAEnB,IAAA,IAAI,gBAAgB,GAChB,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,aAAa;IAElD,IAAI,WAAW,GAAG,CAAC;IACnB,IAAI,cAAc,GAAG,CAAC;AAEtB,IAAA,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;AAC5B;;;;AAIG;AACH,QAAA,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IACvC;AAAO,SAAA,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;AACnC,QAAA,MAAM,GAAG,MAAM,CAAC,IAAI,EAAgB;AAEpC,QAAA,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;AACtB,YAAA,gBAAgB,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAA6B;QACpE;aAAO;AACH;;;;AAIG;AACH,YAAA,gBAAgB,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,MAAiC,CAAC,GAAG,MAAM,GAAG,CAAA,CAAA,CAAG,CAAC;QAC7F;IACJ;AAEA,IAAA,WAAW,GAAG,WAAW,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,WAAW,CAAC;IACzE,cAAc,GAAG,WAAW,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,eAAe,CAAC;IAElE,OAAO,WAAW,GAAG,cAAc;AACvC;;;;"}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
const ScrollOffset = {
|
||||
Enter: [
|
||||
[0, 1],
|
||||
[1, 1],
|
||||
],
|
||||
Exit: [
|
||||
[0, 0],
|
||||
[1, 0],
|
||||
],
|
||||
Any: [
|
||||
[1, 0],
|
||||
[0, 1],
|
||||
],
|
||||
All: [
|
||||
[0, 0],
|
||||
[1, 1],
|
||||
],
|
||||
};
|
||||
|
||||
export { ScrollOffset };
|
||||
//# sourceMappingURL=presets.mjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"presets.mjs","sources":["../../../../../../src/render/dom/scroll/offsets/presets.ts"],"sourcesContent":["import { ProgressIntersection } from \"../types\"\n\nexport const ScrollOffset: Record<string, ProgressIntersection[]> = {\n Enter: [\n [0, 1],\n [1, 1],\n ],\n Exit: [\n [0, 0],\n [1, 0],\n ],\n Any: [\n [1, 0],\n [0, 1],\n ],\n All: [\n [0, 0],\n [1, 1],\n ],\n}\n"],"names":[],"mappings":"AAEO,MAAM,YAAY,GAA2C;AAClE,IAAA,KAAK,EAAE;QACL,CAAC,CAAC,EAAE,CAAC,CAAC;QACN,CAAC,CAAC,EAAE,CAAC,CAAC;AACP,KAAA;AACD,IAAA,IAAI,EAAE;QACJ,CAAC,CAAC,EAAE,CAAC,CAAC;QACN,CAAC,CAAC,EAAE,CAAC,CAAC;AACP,KAAA;AACD,IAAA,GAAG,EAAE;QACH,CAAC,CAAC,EAAE,CAAC,CAAC;QACN,CAAC,CAAC,EAAE,CAAC,CAAC;AACP,KAAA;AACD,IAAA,GAAG,EAAE;QACH,CAAC,CAAC,EAAE,CAAC,CAAC;QACN,CAAC,CAAC,EAAE,CAAC,CAAC;AACP,KAAA;;;;;"}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import { warnOnce } from 'motion-utils';
|
||||
import { updateScrollInfo } from './info.mjs';
|
||||
import { resolveOffsets } from './offsets/index.mjs';
|
||||
|
||||
function measure(container, target = container, info) {
|
||||
/**
|
||||
* Find inset of target within scrollable container
|
||||
*/
|
||||
info.x.targetOffset = 0;
|
||||
info.y.targetOffset = 0;
|
||||
if (target !== container) {
|
||||
let node = target;
|
||||
while (node && node !== container) {
|
||||
info.x.targetOffset += node.offsetLeft;
|
||||
info.y.targetOffset += node.offsetTop;
|
||||
node = node.offsetParent;
|
||||
}
|
||||
}
|
||||
info.x.targetLength =
|
||||
target === container ? target.scrollWidth : target.clientWidth;
|
||||
info.y.targetLength =
|
||||
target === container ? target.scrollHeight : target.clientHeight;
|
||||
info.x.containerLength = container.clientWidth;
|
||||
info.y.containerLength = container.clientHeight;
|
||||
/**
|
||||
* In development mode ensure scroll containers aren't position: static as this makes
|
||||
* it difficult to measure their relative positions.
|
||||
*/
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
if (container && target && target !== container) {
|
||||
warnOnce(getComputedStyle(container).position !== "static", "Please ensure that the container has a non-static position, like 'relative', 'fixed', or 'absolute' to ensure scroll offset is calculated correctly.");
|
||||
}
|
||||
}
|
||||
}
|
||||
function createOnScrollHandler(element, onScroll, info, options = {}) {
|
||||
return {
|
||||
measure: (time) => {
|
||||
measure(element, options.target, info);
|
||||
updateScrollInfo(element, info, time);
|
||||
if (options.offset || options.target) {
|
||||
resolveOffsets(element, info, options);
|
||||
}
|
||||
},
|
||||
notify: () => onScroll(info),
|
||||
};
|
||||
}
|
||||
|
||||
export { createOnScrollHandler };
|
||||
//# sourceMappingURL=on-scroll-handler.mjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"on-scroll-handler.mjs","sources":["../../../../../src/render/dom/scroll/on-scroll-handler.ts"],"sourcesContent":["import { warnOnce } from \"motion-utils\"\nimport { updateScrollInfo } from \"./info\"\nimport { resolveOffsets } from \"./offsets/index\"\nimport {\n OnScrollHandler,\n OnScrollInfo,\n ScrollInfo,\n ScrollInfoOptions,\n} from \"./types\"\n\nfunction measure(\n container: Element,\n target: Element = container,\n info: ScrollInfo\n) {\n /**\n * Find inset of target within scrollable container\n */\n info.x.targetOffset = 0\n info.y.targetOffset = 0\n if (target !== container) {\n let node = target as HTMLElement\n while (node && node !== container) {\n info.x.targetOffset += node.offsetLeft\n info.y.targetOffset += node.offsetTop\n node = node.offsetParent as HTMLElement\n }\n }\n\n info.x.targetLength =\n target === container ? target.scrollWidth : target.clientWidth\n info.y.targetLength =\n target === container ? target.scrollHeight : target.clientHeight\n info.x.containerLength = container.clientWidth\n info.y.containerLength = container.clientHeight\n\n /**\n * In development mode ensure scroll containers aren't position: static as this makes\n * it difficult to measure their relative positions.\n */\n if (process.env.NODE_ENV !== \"production\") {\n if (container && target && target !== container) {\n warnOnce(\n getComputedStyle(container).position !== \"static\",\n \"Please ensure that the container has a non-static position, like 'relative', 'fixed', or 'absolute' to ensure scroll offset is calculated correctly.\"\n )\n }\n }\n}\n\nexport function createOnScrollHandler(\n element: Element,\n onScroll: OnScrollInfo,\n info: ScrollInfo,\n options: ScrollInfoOptions = {}\n): OnScrollHandler {\n return {\n measure: (time) => {\n measure(element, options.target, info)\n updateScrollInfo(element, info, time)\n\n if (options.offset || options.target) {\n resolveOffsets(element, info, options)\n }\n },\n notify: () => onScroll(info),\n }\n}\n"],"names":[],"mappings":";;;;AAUA,SAAS,OAAO,CACZ,SAAkB,EAClB,MAAA,GAAkB,SAAS,EAC3B,IAAgB,EAAA;AAEhB;;AAEG;AACH,IAAA,IAAI,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC;AACvB,IAAA,IAAI,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC;AACvB,IAAA,IAAI,MAAM,KAAK,SAAS,EAAE;QACtB,IAAI,IAAI,GAAG,MAAqB;AAChC,QAAA,OAAO,IAAI,IAAI,IAAI,KAAK,SAAS,EAAE;YAC/B,IAAI,CAAC,CAAC,CAAC,YAAY,IAAI,IAAI,CAAC,UAAU;YACtC,IAAI,CAAC,CAAC,CAAC,YAAY,IAAI,IAAI,CAAC,SAAS;AACrC,YAAA,IAAI,GAAG,IAAI,CAAC,YAA2B;QAC3C;IACJ;IAEA,IAAI,CAAC,CAAC,CAAC,YAAY;AACf,QAAA,MAAM,KAAK,SAAS,GAAG,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW;IAClE,IAAI,CAAC,CAAC,CAAC,YAAY;AACf,QAAA,MAAM,KAAK,SAAS,GAAG,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY;IACpE,IAAI,CAAC,CAAC,CAAC,eAAe,GAAG,SAAS,CAAC,WAAW;IAC9C,IAAI,CAAC,CAAC,CAAC,eAAe,GAAG,SAAS,CAAC,YAAY;AAE/C;;;AAGG;IACH,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE;QACvC,IAAI,SAAS,IAAI,MAAM,IAAI,MAAM,KAAK,SAAS,EAAE;AAC7C,YAAA,QAAQ,CACJ,gBAAgB,CAAC,SAAS,CAAC,CAAC,QAAQ,KAAK,QAAQ,EACjD,sJAAsJ,CACzJ;QACL;IACJ;AACJ;AAEM,SAAU,qBAAqB,CACjC,OAAgB,EAChB,QAAsB,EACtB,IAAgB,EAChB,OAAA,GAA6B,EAAE,EAAA;IAE/B,OAAO;AACH,QAAA,OAAO,EAAE,CAAC,IAAI,KAAI;YACd,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC;AACtC,YAAA,gBAAgB,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;YAErC,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE;AAClC,gBAAA,cAAc,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC;YAC1C;QACJ,CAAC;AACD,QAAA,MAAM,EAAE,MAAM,QAAQ,CAAC,IAAI,CAAC;KAC/B;AACL;;;;"}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
import { resize, frame, cancelFrame, frameData } from 'motion-dom';
|
||||
import { noop } from 'motion-utils';
|
||||
import { createScrollInfo } from './info.mjs';
|
||||
import { createOnScrollHandler } from './on-scroll-handler.mjs';
|
||||
|
||||
const scrollListeners = new WeakMap();
|
||||
const resizeListeners = new WeakMap();
|
||||
const onScrollHandlers = new WeakMap();
|
||||
const scrollSize = new WeakMap();
|
||||
const dimensionCheckProcesses = new WeakMap();
|
||||
const getEventTarget = (element) => element === document.scrollingElement ? window : element;
|
||||
function scrollInfo(onScroll, { container = document.scrollingElement, trackContentSize = false, ...options } = {}) {
|
||||
if (!container)
|
||||
return noop;
|
||||
let containerHandlers = onScrollHandlers.get(container);
|
||||
/**
|
||||
* Get the onScroll handlers for this container.
|
||||
* If one isn't found, create a new one.
|
||||
*/
|
||||
if (!containerHandlers) {
|
||||
containerHandlers = new Set();
|
||||
onScrollHandlers.set(container, containerHandlers);
|
||||
}
|
||||
/**
|
||||
* Create a new onScroll handler for the provided callback.
|
||||
*/
|
||||
const info = createScrollInfo();
|
||||
const containerHandler = createOnScrollHandler(container, onScroll, info, options);
|
||||
containerHandlers.add(containerHandler);
|
||||
/**
|
||||
* Check if there's a scroll event listener for this container.
|
||||
* If not, create one.
|
||||
*/
|
||||
if (!scrollListeners.has(container)) {
|
||||
const measureAll = () => {
|
||||
for (const handler of containerHandlers) {
|
||||
handler.measure(frameData.timestamp);
|
||||
}
|
||||
frame.preUpdate(notifyAll);
|
||||
};
|
||||
const notifyAll = () => {
|
||||
for (const handler of containerHandlers) {
|
||||
handler.notify();
|
||||
}
|
||||
};
|
||||
const listener = () => frame.read(measureAll);
|
||||
scrollListeners.set(container, listener);
|
||||
const target = getEventTarget(container);
|
||||
window.addEventListener("resize", listener);
|
||||
if (container !== document.documentElement) {
|
||||
resizeListeners.set(container, resize(container, listener));
|
||||
}
|
||||
target.addEventListener("scroll", listener);
|
||||
listener();
|
||||
}
|
||||
/**
|
||||
* Enable content size tracking if requested and not already enabled.
|
||||
*/
|
||||
if (trackContentSize && !dimensionCheckProcesses.has(container)) {
|
||||
const listener = scrollListeners.get(container);
|
||||
// Store initial scroll dimensions (object is reused to avoid allocation)
|
||||
const size = {
|
||||
width: container.scrollWidth,
|
||||
height: container.scrollHeight,
|
||||
};
|
||||
scrollSize.set(container, size);
|
||||
// Add frame-based scroll dimension checking to detect content changes
|
||||
const checkScrollDimensions = () => {
|
||||
const newWidth = container.scrollWidth;
|
||||
const newHeight = container.scrollHeight;
|
||||
if (size.width !== newWidth || size.height !== newHeight) {
|
||||
listener();
|
||||
size.width = newWidth;
|
||||
size.height = newHeight;
|
||||
}
|
||||
};
|
||||
// Schedule with keepAlive=true to run every frame
|
||||
const dimensionCheckProcess = frame.read(checkScrollDimensions, true);
|
||||
dimensionCheckProcesses.set(container, dimensionCheckProcess);
|
||||
}
|
||||
const listener = scrollListeners.get(container);
|
||||
frame.read(listener, false, true);
|
||||
return () => {
|
||||
cancelFrame(listener);
|
||||
/**
|
||||
* Check if we even have any handlers for this container.
|
||||
*/
|
||||
const currentHandlers = onScrollHandlers.get(container);
|
||||
if (!currentHandlers)
|
||||
return;
|
||||
currentHandlers.delete(containerHandler);
|
||||
if (currentHandlers.size)
|
||||
return;
|
||||
/**
|
||||
* If no more handlers, remove the scroll listener too.
|
||||
*/
|
||||
const scrollListener = scrollListeners.get(container);
|
||||
scrollListeners.delete(container);
|
||||
if (scrollListener) {
|
||||
getEventTarget(container).removeEventListener("scroll", scrollListener);
|
||||
resizeListeners.get(container)?.();
|
||||
window.removeEventListener("resize", scrollListener);
|
||||
}
|
||||
// Clean up scroll dimension checking
|
||||
const dimensionCheckProcess = dimensionCheckProcesses.get(container);
|
||||
if (dimensionCheckProcess) {
|
||||
cancelFrame(dimensionCheckProcess);
|
||||
dimensionCheckProcesses.delete(container);
|
||||
}
|
||||
scrollSize.delete(container);
|
||||
};
|
||||
}
|
||||
|
||||
export { scrollInfo };
|
||||
//# sourceMappingURL=track.mjs.map
|
||||
+1
File diff suppressed because one or more lines are too long
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
import { supportsViewTimeline, supportsScrollTimeline } from 'motion-dom';
|
||||
|
||||
function canUseNativeTimeline(target) {
|
||||
if (typeof window === "undefined")
|
||||
return false;
|
||||
return target ? supportsViewTimeline() : supportsScrollTimeline();
|
||||
}
|
||||
|
||||
export { canUseNativeTimeline };
|
||||
//# sourceMappingURL=can-use-native-timeline.mjs.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"can-use-native-timeline.mjs","sources":["../../../../../../src/render/dom/scroll/utils/can-use-native-timeline.ts"],"sourcesContent":["import { supportsScrollTimeline, supportsViewTimeline } from \"motion-dom\"\n\nexport function canUseNativeTimeline(target?: Element) {\n if (typeof window === \"undefined\") return false\n return target ? supportsViewTimeline() : supportsScrollTimeline()\n}\n"],"names":[],"mappings":";;AAEM,SAAU,oBAAoB,CAAC,MAAgB,EAAA;IACjD,IAAI,OAAO,MAAM,KAAK,WAAW;AAAE,QAAA,OAAO,KAAK;IAC/C,OAAO,MAAM,GAAG,oBAAoB,EAAE,GAAG,sBAAsB,EAAE;AACrE;;;;"}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import { scrollInfo } from '../track.mjs';
|
||||
import { canUseNativeTimeline } from './can-use-native-timeline.mjs';
|
||||
import { offsetToViewTimelineRange } from './offset-to-range.mjs';
|
||||
|
||||
const timelineCache = new Map();
|
||||
function scrollTimelineFallback(options) {
|
||||
const currentTime = { value: 0 };
|
||||
const cancel = scrollInfo((info) => {
|
||||
currentTime.value = info[options.axis].progress * 100;
|
||||
}, options);
|
||||
return { currentTime, cancel };
|
||||
}
|
||||
function getTimeline({ source, container, ...options }) {
|
||||
const { axis } = options;
|
||||
if (source)
|
||||
container = source;
|
||||
let containerCache = timelineCache.get(container);
|
||||
if (!containerCache) {
|
||||
containerCache = new Map();
|
||||
timelineCache.set(container, containerCache);
|
||||
}
|
||||
const targetKey = options.target ?? "self";
|
||||
let targetCache = containerCache.get(targetKey);
|
||||
if (!targetCache) {
|
||||
targetCache = {};
|
||||
containerCache.set(targetKey, targetCache);
|
||||
}
|
||||
const axisKey = axis + (options.offset ?? []).join(",");
|
||||
if (!targetCache[axisKey]) {
|
||||
if (options.target && canUseNativeTimeline(options.target)) {
|
||||
const range = offsetToViewTimelineRange(options.offset);
|
||||
if (range) {
|
||||
targetCache[axisKey] = new ViewTimeline({
|
||||
subject: options.target,
|
||||
axis,
|
||||
});
|
||||
}
|
||||
else {
|
||||
targetCache[axisKey] = scrollTimelineFallback({
|
||||
container,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (canUseNativeTimeline()) {
|
||||
targetCache[axisKey] = new ScrollTimeline({
|
||||
source: container,
|
||||
axis,
|
||||
});
|
||||
}
|
||||
else {
|
||||
targetCache[axisKey] = scrollTimelineFallback({
|
||||
container,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
}
|
||||
return targetCache[axisKey];
|
||||
}
|
||||
|
||||
export { getTimeline };
|
||||
//# sourceMappingURL=get-timeline.mjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"get-timeline.mjs","sources":["../../../../../../src/render/dom/scroll/utils/get-timeline.ts"],"sourcesContent":["import { ProgressTimeline } from \"motion-dom\"\nimport { scrollInfo } from \"../track\"\nimport { ScrollOptionsWithDefaults } from \"../types\"\nimport { canUseNativeTimeline } from \"./can-use-native-timeline\"\nimport { offsetToViewTimelineRange } from \"./offset-to-range\"\n\ndeclare class ScrollTimeline implements ProgressTimeline {\n constructor(options: ScrollOptions)\n\n currentTime: null | { value: number }\n\n cancel?: VoidFunction\n}\n\ndeclare class ViewTimeline implements ProgressTimeline {\n constructor(options: { subject: Element; axis?: string })\n\n currentTime: null | { value: number }\n\n cancel?: VoidFunction\n}\n\nconst timelineCache = new Map<\n Element,\n Map<Element | \"self\", Record<string, ProgressTimeline>>\n>()\n\nfunction scrollTimelineFallback(options: ScrollOptionsWithDefaults) {\n const currentTime = { value: 0 }\n\n const cancel = scrollInfo((info) => {\n currentTime.value = info[options.axis!].progress * 100\n }, options)\n\n return { currentTime, cancel }\n}\n\nexport function getTimeline({\n source,\n container,\n ...options\n}: ScrollOptionsWithDefaults): ProgressTimeline {\n const { axis } = options\n\n if (source) container = source\n\n let containerCache = timelineCache.get(container)\n if (!containerCache) {\n containerCache = new Map()\n timelineCache.set(container, containerCache)\n }\n\n const targetKey = options.target ?? \"self\"\n let targetCache = containerCache.get(targetKey)\n if (!targetCache) {\n targetCache = {}\n containerCache.set(targetKey, targetCache)\n }\n\n const axisKey = axis + (options.offset ?? []).join(\",\")\n\n if (!targetCache[axisKey]) {\n if (options.target && canUseNativeTimeline(options.target)) {\n const range = offsetToViewTimelineRange(options.offset)\n if (range) {\n targetCache[axisKey] = new ViewTimeline({\n subject: options.target,\n axis,\n })\n } else {\n targetCache[axisKey] = scrollTimelineFallback({\n container,\n ...options,\n })\n }\n } else if (canUseNativeTimeline()) {\n targetCache[axisKey] = new ScrollTimeline({\n source: container,\n axis,\n } as any)\n } else {\n targetCache[axisKey] = scrollTimelineFallback({\n container,\n ...options,\n })\n }\n }\n\n return targetCache[axisKey]!\n}\n"],"names":[],"mappings":";;;;AAsBA,MAAM,aAAa,GAAG,IAAI,GAAG,EAG1B;AAEH,SAAS,sBAAsB,CAAC,OAAkC,EAAA;AAC9D,IAAA,MAAM,WAAW,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE;AAEhC,IAAA,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,IAAI,KAAI;AAC/B,QAAA,WAAW,CAAC,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAK,CAAC,CAAC,QAAQ,GAAG,GAAG;IAC1D,CAAC,EAAE,OAAO,CAAC;AAEX,IAAA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE;AAClC;AAEM,SAAU,WAAW,CAAC,EACxB,MAAM,EACN,SAAS,EACT,GAAG,OAAO,EACc,EAAA;AACxB,IAAA,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO;AAExB,IAAA,IAAI,MAAM;QAAE,SAAS,GAAG,MAAM;IAE9B,IAAI,cAAc,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC;IACjD,IAAI,CAAC,cAAc,EAAE;AACjB,QAAA,cAAc,GAAG,IAAI,GAAG,EAAE;AAC1B,QAAA,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,cAAc,CAAC;IAChD;AAEA,IAAA,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,IAAI,MAAM;IAC1C,IAAI,WAAW,GAAG,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC;IAC/C,IAAI,CAAC,WAAW,EAAE;QACd,WAAW,GAAG,EAAE;AAChB,QAAA,cAAc,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,CAAC;IAC9C;AAEA,IAAA,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC;AAEvD,IAAA,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE;QACvB,IAAI,OAAO,CAAC,MAAM,IAAI,oBAAoB,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;YACxD,MAAM,KAAK,GAAG,yBAAyB,CAAC,OAAO,CAAC,MAAM,CAAC;YACvD,IAAI,KAAK,EAAE;AACP,gBAAA,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI,YAAY,CAAC;oBACpC,OAAO,EAAE,OAAO,CAAC,MAAM;oBACvB,IAAI;AACP,iBAAA,CAAC;YACN;iBAAO;AACH,gBAAA,WAAW,CAAC,OAAO,CAAC,GAAG,sBAAsB,CAAC;oBAC1C,SAAS;AACT,oBAAA,GAAG,OAAO;AACb,iBAAA,CAAC;YACN;QACJ;aAAO,IAAI,oBAAoB,EAAE,EAAE;AAC/B,YAAA,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI,cAAc,CAAC;AACtC,gBAAA,MAAM,EAAE,SAAS;gBACjB,IAAI;AACA,aAAA,CAAC;QACb;aAAO;AACH,YAAA,WAAW,CAAC,OAAO,CAAC,GAAG,sBAAsB,CAAC;gBAC1C,SAAS;AACT,gBAAA,GAAG,OAAO;AACb,aAAA,CAAC;QACN;IACJ;AAEA,IAAA,OAAO,WAAW,CAAC,OAAO,CAAE;AAChC;;;;"}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
import { ScrollOffset } from '../offsets/presets.mjs';
|
||||
|
||||
/**
|
||||
* Maps from ProgressIntersection pairs used by Motion's preset offsets to
|
||||
* ViewTimeline named ranges. Returns undefined for unrecognised patterns,
|
||||
* which signals the caller to fall back to JS-based scroll tracking.
|
||||
*/
|
||||
const presets = [
|
||||
[ScrollOffset.Enter, "entry"],
|
||||
[ScrollOffset.Exit, "exit"],
|
||||
[ScrollOffset.Any, "cover"],
|
||||
[ScrollOffset.All, "contain"],
|
||||
];
|
||||
const stringToProgress = {
|
||||
start: 0,
|
||||
end: 1,
|
||||
};
|
||||
function parseStringOffset(s) {
|
||||
const parts = s.trim().split(/\s+/);
|
||||
if (parts.length !== 2)
|
||||
return undefined;
|
||||
const a = stringToProgress[parts[0]];
|
||||
const b = stringToProgress[parts[1]];
|
||||
if (a === undefined || b === undefined)
|
||||
return undefined;
|
||||
return [a, b];
|
||||
}
|
||||
function normaliseOffset(offset) {
|
||||
if (offset.length !== 2)
|
||||
return undefined;
|
||||
const result = [];
|
||||
for (const item of offset) {
|
||||
if (Array.isArray(item)) {
|
||||
result.push(item);
|
||||
}
|
||||
else if (typeof item === "string") {
|
||||
const parsed = parseStringOffset(item);
|
||||
if (!parsed)
|
||||
return undefined;
|
||||
result.push(parsed);
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function matchesPreset(offset, preset) {
|
||||
const normalised = normaliseOffset(offset);
|
||||
if (!normalised)
|
||||
return false;
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const o = normalised[i];
|
||||
const p = preset[i];
|
||||
if (o[0] !== p[0] || o[1] !== p[1])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function offsetToViewTimelineRange(offset) {
|
||||
if (!offset) {
|
||||
return { rangeStart: "contain 0%", rangeEnd: "contain 100%" };
|
||||
}
|
||||
for (const [preset, name] of presets) {
|
||||
if (matchesPreset(offset, preset)) {
|
||||
return { rangeStart: `${name} 0%`, rangeEnd: `${name} 100%` };
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export { offsetToViewTimelineRange };
|
||||
//# sourceMappingURL=offset-to-range.mjs.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"offset-to-range.mjs","sources":["../../../../../../src/render/dom/scroll/utils/offset-to-range.ts"],"sourcesContent":["import { ScrollOffset as ScrollOffsetPresets } from \"../offsets/presets\"\nimport { ProgressIntersection, ScrollOffset } from \"../types\"\n\ninterface ViewTimelineRange {\n rangeStart: string\n rangeEnd: string\n}\n\n/**\n * Maps from ProgressIntersection pairs used by Motion's preset offsets to\n * ViewTimeline named ranges. Returns undefined for unrecognised patterns,\n * which signals the caller to fall back to JS-based scroll tracking.\n */\nconst presets: [ProgressIntersection[], string][] = [\n [ScrollOffsetPresets.Enter, \"entry\"],\n [ScrollOffsetPresets.Exit, \"exit\"],\n [ScrollOffsetPresets.Any, \"cover\"],\n [ScrollOffsetPresets.All, \"contain\"],\n]\n\nconst stringToProgress: Record<string, number> = {\n start: 0,\n end: 1,\n}\n\nfunction parseStringOffset(\n s: string\n): ProgressIntersection | undefined {\n const parts = s.trim().split(/\\s+/)\n if (parts.length !== 2) return undefined\n const a = stringToProgress[parts[0]]\n const b = stringToProgress[parts[1]]\n if (a === undefined || b === undefined) return undefined\n return [a, b]\n}\n\nfunction normaliseOffset(offset: ScrollOffset): ProgressIntersection[] | undefined {\n if (offset.length !== 2) return undefined\n const result: ProgressIntersection[] = []\n for (const item of offset) {\n if (Array.isArray(item)) {\n result.push(item as ProgressIntersection)\n } else if (typeof item === \"string\") {\n const parsed = parseStringOffset(item)\n if (!parsed) return undefined\n result.push(parsed)\n } else {\n return undefined\n }\n }\n return result\n}\n\nfunction matchesPreset(\n offset: ScrollOffset,\n preset: ProgressIntersection[]\n): boolean {\n const normalised = normaliseOffset(offset)\n if (!normalised) return false\n\n for (let i = 0; i < 2; i++) {\n const o = normalised[i]\n const p = preset[i]\n if (o[0] !== p[0] || o[1] !== p[1]) return false\n }\n return true\n}\n\nexport function offsetToViewTimelineRange(\n offset?: ScrollOffset\n): ViewTimelineRange | undefined {\n if (!offset) {\n return { rangeStart: \"contain 0%\", rangeEnd: \"contain 100%\" }\n }\n\n for (const [preset, name] of presets) {\n if (matchesPreset(offset, preset)) {\n return { rangeStart: `${name} 0%`, rangeEnd: `${name} 100%` }\n }\n }\n\n return undefined\n}\n"],"names":["ScrollOffsetPresets"],"mappings":";;AAQA;;;;AAIG;AACH,MAAM,OAAO,GAAuC;AAChD,IAAA,CAACA,YAAmB,CAAC,KAAK,EAAE,OAAO,CAAC;AACpC,IAAA,CAACA,YAAmB,CAAC,IAAI,EAAE,MAAM,CAAC;AAClC,IAAA,CAACA,YAAmB,CAAC,GAAG,EAAE,OAAO,CAAC;AAClC,IAAA,CAACA,YAAmB,CAAC,GAAG,EAAE,SAAS,CAAC;CACvC;AAED,MAAM,gBAAgB,GAA2B;AAC7C,IAAA,KAAK,EAAE,CAAC;AACR,IAAA,GAAG,EAAE,CAAC;CACT;AAED,SAAS,iBAAiB,CACtB,CAAS,EAAA;IAET,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC;AACnC,IAAA,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,SAAS;IACxC,MAAM,CAAC,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACpC,MAAM,CAAC,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACpC,IAAA,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,SAAS;AAAE,QAAA,OAAO,SAAS;AACxD,IAAA,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;AACjB;AAEA,SAAS,eAAe,CAAC,MAAoB,EAAA;AACzC,IAAA,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,SAAS;IACzC,MAAM,MAAM,GAA2B,EAAE;AACzC,IAAA,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE;AACvB,QAAA,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;AACrB,YAAA,MAAM,CAAC,IAAI,CAAC,IAA4B,CAAC;QAC7C;AAAO,aAAA,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;AACjC,YAAA,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC;AACtC,YAAA,IAAI,CAAC,MAAM;AAAE,gBAAA,OAAO,SAAS;AAC7B,YAAA,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;QACvB;aAAO;AACH,YAAA,OAAO,SAAS;QACpB;IACJ;AACA,IAAA,OAAO,MAAM;AACjB;AAEA,SAAS,aAAa,CAClB,MAAoB,EACpB,MAA8B,EAAA;AAE9B,IAAA,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC;AAC1C,IAAA,IAAI,CAAC,UAAU;AAAE,QAAA,OAAO,KAAK;AAE7B,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;AACxB,QAAA,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;AACvB,QAAA,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;AACnB,QAAA,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAAE,YAAA,OAAO,KAAK;IACpD;AACA,IAAA,OAAO,IAAI;AACf;AAEM,SAAU,yBAAyB,CACrC,MAAqB,EAAA;IAErB,IAAI,CAAC,MAAM,EAAE;QACT,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE;IACjE;IAEA,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,OAAO,EAAE;AAClC,QAAA,IAAI,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;AAC/B,YAAA,OAAO,EAAE,UAAU,EAAE,CAAA,EAAG,IAAI,CAAA,GAAA,CAAK,EAAE,QAAQ,EAAE,CAAA,EAAG,IAAI,CAAA,KAAA,CAAO,EAAE;QACjE;IACJ;AAEA,IAAA,OAAO,SAAS;AACpB;;;;"}
|
||||
Reference in New Issue
Block a user