framer-motion 9.1.0 → 9.1.2
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 +15 -15
- package/dist/cjs/index.js +605 -116
- package/dist/es/animation/hooks/animation-controls.mjs +1 -1
- package/dist/es/animation/index.mjs +1 -1
- package/dist/es/animation/legacy-popmotion/find-spring.mjs +1 -1
- package/dist/es/animation/legacy-popmotion/keyframes.mjs +3 -8
- package/dist/es/animation/utils/easing.mjs +1 -1
- package/dist/es/components/AnimateSharedLayout.mjs +2 -2
- package/dist/es/components/Reorder/Group.mjs +1 -1
- package/dist/es/components/Reorder/Item.mjs +1 -1
- package/dist/es/gestures/drag/VisualElementDragControls.mjs +3 -4
- package/dist/es/gestures/focus.mjs +2 -3
- package/dist/es/gestures/hover.mjs +1 -2
- package/dist/es/gestures/press.mjs +2 -3
- package/dist/es/index.mjs +1 -0
- package/dist/es/motion/features/animation/exit.mjs +1 -2
- package/dist/es/motion/features/viewport/index.mjs +2 -3
- package/dist/es/render/VisualElement.mjs +3 -4
- package/dist/es/render/dom/resize/handle-element.mjs +64 -0
- package/dist/es/render/dom/resize/handle-window.mjs +30 -0
- package/dist/es/render/dom/resize/index.mjs +8 -0
- package/dist/es/render/dom/scroll/index.mjs +74 -0
- package/dist/es/render/dom/scroll/info.mjs +56 -0
- package/dist/es/render/dom/scroll/offsets/edge.mjs +45 -0
- package/dist/es/render/dom/scroll/offsets/index.mjs +54 -0
- package/dist/es/render/dom/scroll/offsets/inset.mjs +25 -0
- package/dist/es/render/dom/scroll/offsets/offset.mjs +35 -0
- package/dist/es/render/dom/scroll/offsets/presets.mjs +20 -0
- package/dist/es/render/dom/scroll/on-scroll-handler.mjs +38 -0
- package/dist/es/render/dom/utils/css-variables-conversion.mjs +5 -7
- package/dist/es/render/dom/utils/is-css-variable.mjs +4 -7
- package/dist/es/render/dom/utils/resolve-element.mjs +21 -0
- package/dist/es/render/dom/utils/unit-conversion.mjs +2 -11
- package/dist/es/render/dom/viewport/index.mjs +52 -0
- package/dist/es/render/html/HTMLVisualElement.mjs +2 -2
- package/dist/es/render/html/utils/build-styles.mjs +2 -2
- package/dist/es/render/utils/animation-state.mjs +16 -21
- package/dist/es/render/utils/is-controlling-variants.mjs +1 -10
- package/dist/es/render/utils/motion-values.mjs +1 -1
- package/dist/es/render/utils/variant-props.mjs +12 -0
- package/dist/es/utils/errors.mjs +18 -0
- package/dist/es/utils/interpolate.mjs +1 -1
- package/dist/es/utils/mix-color.mjs +1 -1
- package/dist/es/utils/mix-complex.mjs +1 -1
- package/dist/es/utils/offsets/default.mjs +9 -0
- package/dist/es/utils/offsets/fill.mjs +12 -0
- package/dist/es/utils/offsets/time.mjs +5 -0
- package/dist/es/utils/use-in-view.mjs +1 -1
- package/dist/es/value/index.mjs +1 -1
- package/dist/es/value/use-inverted-scale.mjs +1 -1
- package/dist/es/value/use-scroll.mjs +2 -2
- package/dist/es/value/use-will-change/index.mjs +2 -2
- package/dist/framer-motion.dev.js +238 -370
- package/dist/framer-motion.js +1 -1
- package/dist/index.d.ts +12 -3
- package/dist/projection.dev.js +53 -78
- package/dist/size-rollup-dom-animation-assets.js +1 -1
- package/dist/size-rollup-dom-animation.js +1 -1
- package/dist/size-rollup-dom-max-assets.js +1 -1
- package/dist/size-rollup-dom-max.js +1 -1
- package/dist/size-rollup-m.js +1 -1
- package/dist/size-rollup-motion.js +1 -1
- package/dist/size-webpack-dom-animation.js +1 -1
- package/dist/size-webpack-dom-max.js +1 -1
- package/dist/size-webpack-m.js +1 -1
- package/dist/three-entry.d.ts +1 -1
- package/package.json +9 -11
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { warning } from '
|
|
1
|
+
import { warning } from '../utils/errors.mjs';
|
|
2
2
|
import { secondsToMilliseconds } from '../utils/time-conversion.mjs';
|
|
3
3
|
import { instantAnimationState } from '../utils/use-instant-transition-state.mjs';
|
|
4
4
|
import { createAcceleratedAnimation } from './waapi/create-accelerated-animation.mjs';
|
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import { easeInOut } from '../../easing/ease.mjs';
|
|
2
2
|
import { interpolate } from '../../utils/interpolate.mjs';
|
|
3
|
+
import { defaultOffset } from '../../utils/offsets/default.mjs';
|
|
4
|
+
import { convertOffsetToTimes } from '../../utils/offsets/time.mjs';
|
|
3
5
|
import { isEasingArray, easingDefinitionToFunction } from '../utils/easing.mjs';
|
|
4
6
|
|
|
5
7
|
function defaultEasing(values, easing) {
|
|
6
8
|
return values.map(() => easing || easeInOut).splice(0, values.length - 1);
|
|
7
9
|
}
|
|
8
|
-
function defaultOffset(values) {
|
|
9
|
-
const numValues = values.length;
|
|
10
|
-
return values.map((_value, i) => i !== 0 ? i / (numValues - 1) : 0);
|
|
11
|
-
}
|
|
12
|
-
function convertOffsetToTimes(offset, duration) {
|
|
13
|
-
return offset.map((o) => o * duration);
|
|
14
|
-
}
|
|
15
10
|
function keyframes({ keyframes: keyframeValues, ease = easeInOut, times, duration = 300, }) {
|
|
16
11
|
keyframeValues = [...keyframeValues];
|
|
17
12
|
/**
|
|
@@ -59,4 +54,4 @@ function keyframes({ keyframes: keyframeValues, ease = easeInOut, times, duratio
|
|
|
59
54
|
};
|
|
60
55
|
}
|
|
61
56
|
|
|
62
|
-
export {
|
|
57
|
+
export { defaultEasing, keyframes };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { invariant } from '
|
|
1
|
+
import { invariant } from '../../utils/errors.mjs';
|
|
2
2
|
import { cubicBezier } from '../../easing/cubic-bezier.mjs';
|
|
3
3
|
import { noop } from '../../utils/noop.mjs';
|
|
4
4
|
import { easeIn, easeInOut, easeOut } from '../../easing/ease.mjs';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { warning } from '
|
|
1
|
+
import { warning } from '../utils/errors.mjs';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { useConstant } from '../utils/use-constant.mjs';
|
|
4
4
|
import { LayoutGroup } from './LayoutGroup/index.mjs';
|
|
5
5
|
|
|
6
6
|
let id = 0;
|
|
7
|
-
const AnimateSharedLayout = ({ children
|
|
7
|
+
const AnimateSharedLayout = ({ children }) => {
|
|
8
8
|
React.useEffect(() => {
|
|
9
9
|
warning(false, "AnimateSharedLayout is deprecated: https://www.framer.com/docs/guide-upgrade/##shared-layout-animations");
|
|
10
10
|
}, []);
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { invariant } from '
|
|
1
|
+
import { invariant } from '../../utils/errors.mjs';
|
|
2
2
|
import { PanSession } from '../pan/PanSession.mjs';
|
|
3
3
|
import { getGlobalLock } from './utils/lock.mjs';
|
|
4
4
|
import { isRefObject } from '../../utils/is-ref-object.mjs';
|
|
5
5
|
import { addPointerEvent } from '../../events/add-pointer-event.mjs';
|
|
6
6
|
import { applyConstraints, calcRelativeConstraints, resolveDragElastic, calcViewportConstraints, defaultElastic, rebaseAxisConstraints, calcOrigin } from './utils/constraints.mjs';
|
|
7
|
-
import { AnimationType } from '../../render/utils/types.mjs';
|
|
8
7
|
import { createBox } from '../../projection/geometry/models.mjs';
|
|
9
8
|
import { eachAxis } from '../../projection/utils/each-axis.mjs';
|
|
10
9
|
import { measurePageBox } from '../../projection/utils/measure.mjs';
|
|
@@ -97,7 +96,7 @@ class VisualElementDragControls {
|
|
|
97
96
|
// Fire onDragStart event
|
|
98
97
|
onDragStart && onDragStart(event, info);
|
|
99
98
|
const { animationState } = this.visualElement;
|
|
100
|
-
animationState && animationState.setActive(AnimationType.Drag
|
|
99
|
+
animationState && animationState.setActive("whileDrag" /* AnimationType.Drag */, true);
|
|
101
100
|
};
|
|
102
101
|
const onMove = (event, info) => {
|
|
103
102
|
// latestPointerEvent = event
|
|
@@ -162,7 +161,7 @@ class VisualElementDragControls {
|
|
|
162
161
|
this.openGlobalLock();
|
|
163
162
|
this.openGlobalLock = null;
|
|
164
163
|
}
|
|
165
|
-
animationState && animationState.setActive(AnimationType.Drag
|
|
164
|
+
animationState && animationState.setActive("whileDrag" /* AnimationType.Drag */, false);
|
|
166
165
|
}
|
|
167
166
|
updateAxis(axis, _point, offset) {
|
|
168
167
|
const { drag } = this.getProps();
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { addDomEvent } from '../events/add-dom-event.mjs';
|
|
2
2
|
import { Feature } from '../motion/features/Feature.mjs';
|
|
3
|
-
import { AnimationType } from '../render/utils/types.mjs';
|
|
4
3
|
import { pipe } from '../utils/pipe.mjs';
|
|
5
4
|
|
|
6
5
|
class FocusGesture extends Feature {
|
|
@@ -24,13 +23,13 @@ class FocusGesture extends Feature {
|
|
|
24
23
|
}
|
|
25
24
|
if (!isFocusVisible || !this.node.animationState)
|
|
26
25
|
return;
|
|
27
|
-
this.node.animationState.setActive(AnimationType.Focus
|
|
26
|
+
this.node.animationState.setActive("whileFocus" /* AnimationType.Focus */, true);
|
|
28
27
|
this.isActive = true;
|
|
29
28
|
}
|
|
30
29
|
onBlur() {
|
|
31
30
|
if (!this.isActive || !this.node.animationState)
|
|
32
31
|
return;
|
|
33
|
-
this.node.animationState.setActive(AnimationType.Focus
|
|
32
|
+
this.node.animationState.setActive("whileFocus" /* AnimationType.Focus */, false);
|
|
34
33
|
this.isActive = false;
|
|
35
34
|
}
|
|
36
35
|
mount() {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { addPointerEvent } from '../events/add-pointer-event.mjs';
|
|
2
|
-
import { AnimationType } from '../render/utils/types.mjs';
|
|
3
2
|
import { pipe } from '../utils/pipe.mjs';
|
|
4
3
|
import { isDragActive } from './drag/utils/lock.mjs';
|
|
5
4
|
import { Feature } from '../motion/features/Feature.mjs';
|
|
@@ -12,7 +11,7 @@ function addHoverEvent(node, isActive) {
|
|
|
12
11
|
return;
|
|
13
12
|
const props = node.getProps();
|
|
14
13
|
if (node.animationState && props.whileHover) {
|
|
15
|
-
node.animationState.setActive(AnimationType.Hover
|
|
14
|
+
node.animationState.setActive("whileHover" /* AnimationType.Hover */, isActive);
|
|
16
15
|
}
|
|
17
16
|
if (props[callbackName]) {
|
|
18
17
|
props[callbackName](event, info);
|
|
@@ -2,7 +2,6 @@ import { extractEventInfo } from '../events/event-info.mjs';
|
|
|
2
2
|
import { addDomEvent } from '../events/add-dom-event.mjs';
|
|
3
3
|
import { addPointerEvent } from '../events/add-pointer-event.mjs';
|
|
4
4
|
import { Feature } from '../motion/features/Feature.mjs';
|
|
5
|
-
import { AnimationType } from '../render/utils/types.mjs';
|
|
6
5
|
import { pipe } from '../utils/pipe.mjs';
|
|
7
6
|
import { isDragActive } from './drag/utils/lock.mjs';
|
|
8
7
|
import { isNodeOrChild } from './utils/is-node-or-child.mjs';
|
|
@@ -74,7 +73,7 @@ class PressGesture extends Feature {
|
|
|
74
73
|
* Ensure we trigger animations before firing event callback
|
|
75
74
|
*/
|
|
76
75
|
if (whileTap && this.node.animationState) {
|
|
77
|
-
this.node.animationState.setActive(AnimationType.Tap
|
|
76
|
+
this.node.animationState.setActive("whileTap" /* AnimationType.Tap */, true);
|
|
78
77
|
}
|
|
79
78
|
onTapStart && onTapStart(event, info);
|
|
80
79
|
}
|
|
@@ -83,7 +82,7 @@ class PressGesture extends Feature {
|
|
|
83
82
|
this.isPressing = false;
|
|
84
83
|
const props = this.node.getProps();
|
|
85
84
|
if (props.whileTap && this.node.animationState) {
|
|
86
|
-
this.node.animationState.setActive(AnimationType.Tap
|
|
85
|
+
this.node.animationState.setActive("whileTap" /* AnimationType.Tap */, false);
|
|
87
86
|
}
|
|
88
87
|
return !isDragActive();
|
|
89
88
|
}
|
package/dist/es/index.mjs
CHANGED
|
@@ -47,6 +47,7 @@ export { buildTransform } from './render/html/utils/build-transform.mjs';
|
|
|
47
47
|
export { clamp } from './utils/clamp.mjs';
|
|
48
48
|
export { delay } from './utils/delay.mjs';
|
|
49
49
|
export { distance, distance2D } from './utils/distance.mjs';
|
|
50
|
+
export { invariant, warning } from './utils/errors.mjs';
|
|
50
51
|
export { interpolate } from './utils/interpolate.mjs';
|
|
51
52
|
export { mix } from './utils/mix.mjs';
|
|
52
53
|
export { pipe } from './utils/pipe.mjs';
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { AnimationType } from '../../../render/utils/types.mjs';
|
|
2
1
|
import { Feature } from '../Feature.mjs';
|
|
3
2
|
|
|
4
3
|
let id = 0;
|
|
@@ -15,7 +14,7 @@ class ExitAnimationFeature extends Feature {
|
|
|
15
14
|
if (!this.node.animationState || isPresent === prevIsPresent) {
|
|
16
15
|
return;
|
|
17
16
|
}
|
|
18
|
-
const exitAnimation = this.node.animationState.setActive(AnimationType.Exit
|
|
17
|
+
const exitAnimation = this.node.animationState.setActive("exit" /* AnimationType.Exit */, !isPresent, { custom: custom !== null && custom !== void 0 ? custom : this.node.getProps().custom });
|
|
19
18
|
if (onExitComplete && !isPresent) {
|
|
20
19
|
exitAnimation.then(() => onExitComplete(this.id));
|
|
21
20
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { AnimationType } from '../../../render/utils/types.mjs';
|
|
2
1
|
import { Feature } from '../Feature.mjs';
|
|
3
2
|
import { observeIntersection } from './observers.mjs';
|
|
4
3
|
|
|
@@ -26,7 +25,7 @@ class InViewFeature extends Feature {
|
|
|
26
25
|
const { onViewportEnter } = this.node.getProps();
|
|
27
26
|
onViewportEnter && onViewportEnter(null);
|
|
28
27
|
if (this.node.animationState) {
|
|
29
|
-
this.node.animationState.setActive(AnimationType.InView
|
|
28
|
+
this.node.animationState.setActive("whileInView" /* AnimationType.InView */, true);
|
|
30
29
|
}
|
|
31
30
|
});
|
|
32
31
|
}
|
|
@@ -63,7 +62,7 @@ class InViewFeature extends Feature {
|
|
|
63
62
|
this.hasEnteredView = true;
|
|
64
63
|
}
|
|
65
64
|
if (this.node.animationState) {
|
|
66
|
-
this.node.animationState.setActive(AnimationType.InView
|
|
65
|
+
this.node.animationState.setActive("whileInView" /* AnimationType.InView */, isIntersecting);
|
|
67
66
|
}
|
|
68
67
|
/**
|
|
69
68
|
* Use the latest committed props rather than the ones in scope
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { sync, cancelSync } from '../frameloop/index.mjs';
|
|
2
|
-
import { warning, invariant } from '
|
|
2
|
+
import { warning, invariant } from '../utils/errors.mjs';
|
|
3
3
|
import { createBox } from '../projection/geometry/models.mjs';
|
|
4
4
|
import { isRefObject } from '../utils/is-ref-object.mjs';
|
|
5
5
|
import { initPrefersReducedMotion } from '../utils/reduced-motion/index.mjs';
|
|
@@ -9,13 +9,13 @@ import { motionValue } from '../value/index.mjs';
|
|
|
9
9
|
import { isWillChangeMotionValue } from '../value/use-will-change/is.mjs';
|
|
10
10
|
import { isMotionValue } from '../value/utils/is-motion-value.mjs';
|
|
11
11
|
import { transformProps } from './html/utils/transform.mjs';
|
|
12
|
-
import { variantPriorityOrder } from './utils/animation-state.mjs';
|
|
13
12
|
import { isControllingVariants, isVariantNode } from './utils/is-controlling-variants.mjs';
|
|
14
13
|
import { isVariantLabel } from './utils/is-variant-label.mjs';
|
|
15
14
|
import { updateMotionValuesFromProps } from './utils/motion-values.mjs';
|
|
16
15
|
import { resolveVariantFromProps } from './utils/resolve-variants.mjs';
|
|
17
16
|
import { warnOnce } from '../utils/warn-once.mjs';
|
|
18
17
|
import { featureDefinitions } from '../motion/features/definitions.mjs';
|
|
18
|
+
import { variantProps } from './utils/variant-props.mjs';
|
|
19
19
|
|
|
20
20
|
const featureNames = Object.keys(featureDefinitions);
|
|
21
21
|
const numFeatures = featureNames.length;
|
|
@@ -28,6 +28,7 @@ const propEventHandlers = [
|
|
|
28
28
|
"LayoutAnimationStart",
|
|
29
29
|
"LayoutAnimationComplete",
|
|
30
30
|
];
|
|
31
|
+
const numVariantProps = variantProps.length;
|
|
31
32
|
/**
|
|
32
33
|
* A VisualElement is an imperative abstraction around UI elements such as
|
|
33
34
|
* HTMLElement, SVGElement, Three.Object3D etc.
|
|
@@ -498,7 +499,5 @@ class VisualElement {
|
|
|
498
499
|
}
|
|
499
500
|
}
|
|
500
501
|
}
|
|
501
|
-
const variantProps = ["initial", ...variantPriorityOrder];
|
|
502
|
-
const numVariantProps = variantProps.length;
|
|
503
502
|
|
|
504
503
|
export { VisualElement };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { resolveElements } from '../utils/resolve-element.mjs';
|
|
2
|
+
|
|
3
|
+
const resizeHandlers = new WeakMap();
|
|
4
|
+
let observer;
|
|
5
|
+
function getElementSize(target, borderBoxSize) {
|
|
6
|
+
if (borderBoxSize) {
|
|
7
|
+
const { inlineSize, blockSize } = borderBoxSize[0];
|
|
8
|
+
return { width: inlineSize, height: blockSize };
|
|
9
|
+
}
|
|
10
|
+
else if (target instanceof SVGElement && "getBBox" in target) {
|
|
11
|
+
return target.getBBox();
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
return {
|
|
15
|
+
width: target.offsetWidth,
|
|
16
|
+
height: target.offsetHeight,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function notifyTarget({ target, contentRect, borderBoxSize, }) {
|
|
21
|
+
var _a;
|
|
22
|
+
(_a = resizeHandlers.get(target)) === null || _a === void 0 ? void 0 : _a.forEach((handler) => {
|
|
23
|
+
handler({
|
|
24
|
+
target,
|
|
25
|
+
contentSize: contentRect,
|
|
26
|
+
get size() {
|
|
27
|
+
return getElementSize(target, borderBoxSize);
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function notifyAll(entries) {
|
|
33
|
+
entries.forEach(notifyTarget);
|
|
34
|
+
}
|
|
35
|
+
function createResizeObserver() {
|
|
36
|
+
if (typeof ResizeObserver === "undefined")
|
|
37
|
+
return;
|
|
38
|
+
observer = new ResizeObserver(notifyAll);
|
|
39
|
+
}
|
|
40
|
+
function resizeElement(target, handler) {
|
|
41
|
+
if (!observer)
|
|
42
|
+
createResizeObserver();
|
|
43
|
+
const elements = resolveElements(target);
|
|
44
|
+
elements.forEach((element) => {
|
|
45
|
+
let elementHandlers = resizeHandlers.get(element);
|
|
46
|
+
if (!elementHandlers) {
|
|
47
|
+
elementHandlers = new Set();
|
|
48
|
+
resizeHandlers.set(element, elementHandlers);
|
|
49
|
+
}
|
|
50
|
+
elementHandlers.add(handler);
|
|
51
|
+
observer === null || observer === void 0 ? void 0 : observer.observe(element);
|
|
52
|
+
});
|
|
53
|
+
return () => {
|
|
54
|
+
elements.forEach((element) => {
|
|
55
|
+
const elementHandlers = resizeHandlers.get(element);
|
|
56
|
+
elementHandlers === null || elementHandlers === void 0 ? void 0 : elementHandlers.delete(handler);
|
|
57
|
+
if (!(elementHandlers === null || elementHandlers === void 0 ? void 0 : elementHandlers.size)) {
|
|
58
|
+
observer === null || observer === void 0 ? void 0 : observer.unobserve(element);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { resizeElement };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const windowCallbacks = new Set();
|
|
2
|
+
let windowResizeHandler;
|
|
3
|
+
function createWindowResizeHandler() {
|
|
4
|
+
windowResizeHandler = () => {
|
|
5
|
+
const size = {
|
|
6
|
+
width: window.innerWidth,
|
|
7
|
+
height: window.innerHeight,
|
|
8
|
+
};
|
|
9
|
+
const info = {
|
|
10
|
+
target: window,
|
|
11
|
+
size,
|
|
12
|
+
contentSize: size,
|
|
13
|
+
};
|
|
14
|
+
windowCallbacks.forEach((callback) => callback(info));
|
|
15
|
+
};
|
|
16
|
+
window.addEventListener("resize", windowResizeHandler);
|
|
17
|
+
}
|
|
18
|
+
function resizeWindow(callback) {
|
|
19
|
+
windowCallbacks.add(callback);
|
|
20
|
+
if (!windowResizeHandler)
|
|
21
|
+
createWindowResizeHandler();
|
|
22
|
+
return () => {
|
|
23
|
+
windowCallbacks.delete(callback);
|
|
24
|
+
if (!windowCallbacks.size && windowResizeHandler) {
|
|
25
|
+
windowResizeHandler = undefined;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { resizeWindow };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { resize } from '../resize/index.mjs';
|
|
2
|
+
import { createScrollInfo } from './info.mjs';
|
|
3
|
+
import { createOnScrollHandler } from './on-scroll-handler.mjs';
|
|
4
|
+
|
|
5
|
+
const scrollListeners = new WeakMap();
|
|
6
|
+
const resizeListeners = new WeakMap();
|
|
7
|
+
const onScrollHandlers = new WeakMap();
|
|
8
|
+
const getEventTarget = (element) => element === document.documentElement ? window : element;
|
|
9
|
+
function scroll(onScroll, { container = document.documentElement, ...options } = {}) {
|
|
10
|
+
let containerHandlers = onScrollHandlers.get(container);
|
|
11
|
+
/**
|
|
12
|
+
* Get the onScroll handlers for this container.
|
|
13
|
+
* If one isn't found, create a new one.
|
|
14
|
+
*/
|
|
15
|
+
if (!containerHandlers) {
|
|
16
|
+
containerHandlers = new Set();
|
|
17
|
+
onScrollHandlers.set(container, containerHandlers);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create a new onScroll handler for the provided callback.
|
|
21
|
+
*/
|
|
22
|
+
const info = createScrollInfo();
|
|
23
|
+
const containerHandler = createOnScrollHandler(container, onScroll, info, options);
|
|
24
|
+
containerHandlers.add(containerHandler);
|
|
25
|
+
/**
|
|
26
|
+
* Check if there's a scroll event listener for this container.
|
|
27
|
+
* If not, create one.
|
|
28
|
+
*/
|
|
29
|
+
if (!scrollListeners.has(container)) {
|
|
30
|
+
const listener = () => {
|
|
31
|
+
const time = performance.now();
|
|
32
|
+
for (const handler of containerHandlers)
|
|
33
|
+
handler.measure();
|
|
34
|
+
for (const handler of containerHandlers)
|
|
35
|
+
handler.update(time);
|
|
36
|
+
for (const handler of containerHandlers)
|
|
37
|
+
handler.notify();
|
|
38
|
+
};
|
|
39
|
+
scrollListeners.set(container, listener);
|
|
40
|
+
const target = getEventTarget(container);
|
|
41
|
+
window.addEventListener("resize", listener, { passive: true });
|
|
42
|
+
if (container !== document.documentElement) {
|
|
43
|
+
resizeListeners.set(container, resize(container, listener));
|
|
44
|
+
}
|
|
45
|
+
target.addEventListener("scroll", listener, { passive: true });
|
|
46
|
+
}
|
|
47
|
+
const listener = scrollListeners.get(container);
|
|
48
|
+
const onLoadProcesss = requestAnimationFrame(listener);
|
|
49
|
+
return () => {
|
|
50
|
+
var _a;
|
|
51
|
+
cancelAnimationFrame(onLoadProcesss);
|
|
52
|
+
/**
|
|
53
|
+
* Check if we even have any handlers for this container.
|
|
54
|
+
*/
|
|
55
|
+
const currentHandlers = onScrollHandlers.get(container);
|
|
56
|
+
if (!currentHandlers)
|
|
57
|
+
return;
|
|
58
|
+
currentHandlers.delete(containerHandler);
|
|
59
|
+
if (currentHandlers.size)
|
|
60
|
+
return;
|
|
61
|
+
/**
|
|
62
|
+
* If no more handlers, remove the scroll listener too.
|
|
63
|
+
*/
|
|
64
|
+
const scrollListener = scrollListeners.get(container);
|
|
65
|
+
scrollListeners.delete(container);
|
|
66
|
+
if (scrollListener) {
|
|
67
|
+
getEventTarget(container).removeEventListener("scroll", scrollListener);
|
|
68
|
+
(_a = resizeListeners.get(container)) === null || _a === void 0 ? void 0 : _a();
|
|
69
|
+
window.removeEventListener("resize", scrollListener);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { scroll };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { progress } from '../../../utils/progress.mjs';
|
|
2
|
+
import { velocityPerSecond } from '../../../utils/velocity-per-second.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A time in milliseconds, beyond which we consider the scroll velocity to be 0.
|
|
6
|
+
*/
|
|
7
|
+
const maxElapsed = 50;
|
|
8
|
+
const createAxisInfo = () => ({
|
|
9
|
+
current: 0,
|
|
10
|
+
offset: [],
|
|
11
|
+
progress: 0,
|
|
12
|
+
scrollLength: 0,
|
|
13
|
+
targetOffset: 0,
|
|
14
|
+
targetLength: 0,
|
|
15
|
+
containerLength: 0,
|
|
16
|
+
velocity: 0,
|
|
17
|
+
});
|
|
18
|
+
const createScrollInfo = () => ({
|
|
19
|
+
time: 0,
|
|
20
|
+
x: createAxisInfo(),
|
|
21
|
+
y: createAxisInfo(),
|
|
22
|
+
});
|
|
23
|
+
const keys = {
|
|
24
|
+
x: {
|
|
25
|
+
length: "Width",
|
|
26
|
+
position: "Left",
|
|
27
|
+
},
|
|
28
|
+
y: {
|
|
29
|
+
length: "Height",
|
|
30
|
+
position: "Top",
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
function updateAxisInfo(element, axisName, info, time) {
|
|
34
|
+
const axis = info[axisName];
|
|
35
|
+
const { length, position } = keys[axisName];
|
|
36
|
+
const prev = axis.current;
|
|
37
|
+
const prevTime = info.time;
|
|
38
|
+
axis.current = element["scroll" + position];
|
|
39
|
+
axis.scrollLength = element["scroll" + length] - element["client" + length];
|
|
40
|
+
axis.offset.length = 0;
|
|
41
|
+
axis.offset[0] = 0;
|
|
42
|
+
axis.offset[1] = axis.scrollLength;
|
|
43
|
+
axis.progress = progress(0, axis.scrollLength, axis.current);
|
|
44
|
+
const elapsed = time - prevTime;
|
|
45
|
+
axis.velocity =
|
|
46
|
+
elapsed > maxElapsed
|
|
47
|
+
? 0
|
|
48
|
+
: velocityPerSecond(axis.current - prev, elapsed);
|
|
49
|
+
}
|
|
50
|
+
function updateScrollInfo(element, info, time) {
|
|
51
|
+
updateAxisInfo(element, "x", info, time);
|
|
52
|
+
updateAxisInfo(element, "y", info, time);
|
|
53
|
+
info.time = time;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { createScrollInfo, updateScrollInfo };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const namedEdges = {
|
|
2
|
+
start: 0,
|
|
3
|
+
center: 0.5,
|
|
4
|
+
end: 1,
|
|
5
|
+
};
|
|
6
|
+
function resolveEdge(edge, length, inset = 0) {
|
|
7
|
+
let delta = 0;
|
|
8
|
+
/**
|
|
9
|
+
* If we have this edge defined as a preset, replace the definition
|
|
10
|
+
* with the numerical value.
|
|
11
|
+
*/
|
|
12
|
+
if (namedEdges[edge] !== undefined) {
|
|
13
|
+
edge = namedEdges[edge];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Handle unit values
|
|
17
|
+
*/
|
|
18
|
+
if (typeof edge === "string") {
|
|
19
|
+
const asNumber = parseFloat(edge);
|
|
20
|
+
if (edge.endsWith("px")) {
|
|
21
|
+
delta = asNumber;
|
|
22
|
+
}
|
|
23
|
+
else if (edge.endsWith("%")) {
|
|
24
|
+
edge = asNumber / 100;
|
|
25
|
+
}
|
|
26
|
+
else if (edge.endsWith("vw")) {
|
|
27
|
+
delta = (asNumber / 100) * document.documentElement.clientWidth;
|
|
28
|
+
}
|
|
29
|
+
else if (edge.endsWith("vh")) {
|
|
30
|
+
delta = (asNumber / 100) * document.documentElement.clientHeight;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
edge = asNumber;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* If the edge is defined as a number, handle as a progress value.
|
|
38
|
+
*/
|
|
39
|
+
if (typeof edge === "number") {
|
|
40
|
+
delta = length * edge;
|
|
41
|
+
}
|
|
42
|
+
return inset + delta;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { namedEdges, resolveEdge };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { calcInset } from './inset.mjs';
|
|
2
|
+
import { ScrollOffset } from './presets.mjs';
|
|
3
|
+
import { resolveOffset } from './offset.mjs';
|
|
4
|
+
import { interpolate } from '../../../../utils/interpolate.mjs';
|
|
5
|
+
import { defaultOffset } from '../../../../utils/offsets/default.mjs';
|
|
6
|
+
|
|
7
|
+
const point = { x: 0, y: 0 };
|
|
8
|
+
function resolveOffsets(container, info, options) {
|
|
9
|
+
let { offset: offsetDefinition = ScrollOffset.All } = options;
|
|
10
|
+
const { target = container, axis = "y" } = options;
|
|
11
|
+
const lengthLabel = axis === "y" ? "height" : "width";
|
|
12
|
+
const inset = target !== container ? calcInset(target, container) : point;
|
|
13
|
+
/**
|
|
14
|
+
* Measure the target and container. If they're the same thing then we
|
|
15
|
+
* use the container's scrollWidth/Height as the target, from there
|
|
16
|
+
* all other calculations can remain the same.
|
|
17
|
+
*/
|
|
18
|
+
const targetSize = target === container
|
|
19
|
+
? { width: container.scrollWidth, height: container.scrollHeight }
|
|
20
|
+
: { width: target.clientWidth, height: target.clientHeight };
|
|
21
|
+
const containerSize = {
|
|
22
|
+
width: container.clientWidth,
|
|
23
|
+
height: container.clientHeight,
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Reset the length of the resolved offset array rather than creating a new one.
|
|
27
|
+
* TODO: More reusable data structures for targetSize/containerSize would also be good.
|
|
28
|
+
*/
|
|
29
|
+
info[axis].offset.length = 0;
|
|
30
|
+
/**
|
|
31
|
+
* Populate the offset array by resolving the user's offset definition into
|
|
32
|
+
* a list of pixel scroll offets.
|
|
33
|
+
*/
|
|
34
|
+
let hasChanged = !info[axis].interpolate;
|
|
35
|
+
const numOffsets = offsetDefinition.length;
|
|
36
|
+
for (let i = 0; i < numOffsets; i++) {
|
|
37
|
+
const offset = resolveOffset(offsetDefinition[i], containerSize[lengthLabel], targetSize[lengthLabel], inset[axis]);
|
|
38
|
+
if (!hasChanged && offset !== info[axis].interpolatorOffsets[i]) {
|
|
39
|
+
hasChanged = true;
|
|
40
|
+
}
|
|
41
|
+
info[axis].offset[i] = offset;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* If the pixel scroll offsets have changed, create a new interpolator function
|
|
45
|
+
* to map scroll value into a progress.
|
|
46
|
+
*/
|
|
47
|
+
if (hasChanged) {
|
|
48
|
+
info[axis].interpolate = interpolate(info[axis].offset, defaultOffset(offsetDefinition));
|
|
49
|
+
info[axis].interpolatorOffsets = [...info[axis].offset];
|
|
50
|
+
}
|
|
51
|
+
info[axis].progress = info[axis].interpolate(info[axis].current);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { resolveOffsets };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
function calcInset(element, container) {
|
|
2
|
+
let inset = { x: 0, y: 0 };
|
|
3
|
+
let current = element;
|
|
4
|
+
while (current && current !== container) {
|
|
5
|
+
if (current instanceof HTMLElement) {
|
|
6
|
+
inset.x += current.offsetLeft;
|
|
7
|
+
inset.y += current.offsetTop;
|
|
8
|
+
current = current.offsetParent;
|
|
9
|
+
}
|
|
10
|
+
else if (current instanceof SVGGraphicsElement && "getBBox" in current) {
|
|
11
|
+
const { top, left } = current.getBBox();
|
|
12
|
+
inset.x += left;
|
|
13
|
+
inset.y += top;
|
|
14
|
+
/**
|
|
15
|
+
* Assign the next parent element as the <svg /> tag.
|
|
16
|
+
*/
|
|
17
|
+
while (current && current.tagName !== "svg") {
|
|
18
|
+
current = current.parentNode;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return inset;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { calcInset };
|