framer-motion 5.1.0 → 5.3.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/CHANGELOG.md +1953 -0
- package/dist/es/motion/features/definitions.mjs +6 -0
- package/dist/es/motion/features/gestures.mjs +2 -0
- package/dist/es/motion/features/viewport/observers.mjs +52 -0
- package/dist/es/motion/features/viewport/use-viewport.mjs +97 -0
- package/dist/es/motion/utils/valid-prop.mjs +4 -0
- package/dist/es/projection/node/create-projection-node.mjs +1 -1
- package/dist/es/render/dom/utils/unit-conversion.mjs +8 -1
- package/dist/es/render/utils/animation-state.mjs +2 -0
- package/dist/es/render/utils/types.mjs +1 -0
- package/dist/es/utils/warn-once.mjs +11 -0
- package/dist/framer-motion.cjs.js +173 -2
- package/dist/framer-motion.dev.js +226 -14
- package/dist/framer-motion.js +1 -1
- package/dist/projection.dev.js +63 -13
- package/dist/size-rollup-dom-animation.js +1 -1
- package/dist/size-rollup-dom-max.js +1 -1
- package/dist/size-rollup-m.js +1 -1
- package/package.json +158 -158
- package/types/motion/features/types.d.ts +2 -0
- package/types/motion/features/viewport/observers.d.ts +3 -0
- package/types/motion/features/viewport/types.d.ts +20 -0
- package/types/motion/features/viewport/use-viewport.d.ts +2 -0
- package/types/motion/types.d.ts +2 -1
- package/types/render/utils/types.d.ts +1 -0
- package/types/utils/warn-once.d.ts +2 -0
|
@@ -135,6 +135,7 @@
|
|
|
135
135
|
"whileTap",
|
|
136
136
|
"whileFocus",
|
|
137
137
|
"whileDrag",
|
|
138
|
+
"whileInView",
|
|
138
139
|
]),
|
|
139
140
|
exit: createDefinition(["exit"]),
|
|
140
141
|
drag: createDefinition(["drag", "dragControls"]),
|
|
@@ -147,6 +148,11 @@
|
|
|
147
148
|
"onPanSessionStart",
|
|
148
149
|
"onPanEnd",
|
|
149
150
|
]),
|
|
151
|
+
inView: createDefinition([
|
|
152
|
+
"whileInView",
|
|
153
|
+
"onViewportEnter",
|
|
154
|
+
"onViewportLeave",
|
|
155
|
+
]),
|
|
150
156
|
};
|
|
151
157
|
function loadFeatures(features) {
|
|
152
158
|
for (var key in features) {
|
|
@@ -929,6 +935,46 @@
|
|
|
929
935
|
return functions ? functions.map(applyDefaultFilter).join(' ') : v;
|
|
930
936
|
} });
|
|
931
937
|
|
|
938
|
+
function hueToRgb(p, q, t) {
|
|
939
|
+
if (t < 0)
|
|
940
|
+
t += 1;
|
|
941
|
+
if (t > 1)
|
|
942
|
+
t -= 1;
|
|
943
|
+
if (t < 1 / 6)
|
|
944
|
+
return p + (q - p) * 6 * t;
|
|
945
|
+
if (t < 1 / 2)
|
|
946
|
+
return q;
|
|
947
|
+
if (t < 2 / 3)
|
|
948
|
+
return p + (q - p) * (2 / 3 - t) * 6;
|
|
949
|
+
return p;
|
|
950
|
+
}
|
|
951
|
+
function hslaToRgba({ hue, saturation, lightness, alpha }) {
|
|
952
|
+
hue /= 360;
|
|
953
|
+
saturation /= 100;
|
|
954
|
+
lightness /= 100;
|
|
955
|
+
let red = 0;
|
|
956
|
+
let green = 0;
|
|
957
|
+
let blue = 0;
|
|
958
|
+
if (!saturation) {
|
|
959
|
+
red = green = blue = lightness;
|
|
960
|
+
}
|
|
961
|
+
else {
|
|
962
|
+
const q = lightness < 0.5
|
|
963
|
+
? lightness * (1 + saturation)
|
|
964
|
+
: lightness + saturation - lightness * saturation;
|
|
965
|
+
const p = 2 * lightness - q;
|
|
966
|
+
red = hueToRgb(p, q, hue + 1 / 3);
|
|
967
|
+
green = hueToRgb(p, q, hue);
|
|
968
|
+
blue = hueToRgb(p, q, hue - 1 / 3);
|
|
969
|
+
}
|
|
970
|
+
return {
|
|
971
|
+
red: Math.round(red * 255),
|
|
972
|
+
green: Math.round(green * 255),
|
|
973
|
+
blue: Math.round(blue * 255),
|
|
974
|
+
alpha,
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
|
|
932
978
|
const mixLinearColor = (from, to, v) => {
|
|
933
979
|
const fromExpo = from * from;
|
|
934
980
|
const toExpo = to * to;
|
|
@@ -938,24 +984,25 @@
|
|
|
938
984
|
const getColorType = (v) => colorTypes.find((type) => type.test(v));
|
|
939
985
|
const notAnimatable = (color) => `'${color}' is not an animatable color. Use the equivalent color code instead.`;
|
|
940
986
|
const mixColor = (from, to) => {
|
|
941
|
-
|
|
942
|
-
|
|
987
|
+
let fromColorType = getColorType(from);
|
|
988
|
+
let toColorType = getColorType(to);
|
|
943
989
|
invariant(!!fromColorType, notAnimatable(from));
|
|
944
990
|
invariant(!!toColorType, notAnimatable(to));
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
|
|
991
|
+
let fromColor = fromColorType.parse(from);
|
|
992
|
+
let toColor = toColorType.parse(to);
|
|
993
|
+
if (fromColorType === hsla) {
|
|
994
|
+
fromColor = hslaToRgba(fromColor);
|
|
995
|
+
fromColorType = rgba;
|
|
996
|
+
}
|
|
997
|
+
if (toColorType === hsla) {
|
|
998
|
+
toColor = hslaToRgba(toColor);
|
|
999
|
+
toColorType = rgba;
|
|
1000
|
+
}
|
|
953
1001
|
const blended = Object.assign({}, fromColor);
|
|
954
|
-
const mixFunc = fromColorType === hsla ? mix : mixLinearColor;
|
|
955
1002
|
return (v) => {
|
|
956
1003
|
for (const key in blended) {
|
|
957
1004
|
if (key !== "alpha") {
|
|
958
|
-
blended[key] =
|
|
1005
|
+
blended[key] = mixLinearColor(fromColor[key], toColor[key], v);
|
|
959
1006
|
}
|
|
960
1007
|
}
|
|
961
1008
|
blended.alpha = mix(fromColor.alpha, toColor.alpha, v);
|
|
@@ -3178,7 +3225,7 @@
|
|
|
3178
3225
|
node.updateScroll();
|
|
3179
3226
|
}
|
|
3180
3227
|
var _d = this.options, layoutId = _d.layoutId, layout = _d.layout;
|
|
3181
|
-
if (
|
|
3228
|
+
if (layoutId === undefined && !layout)
|
|
3182
3229
|
return;
|
|
3183
3230
|
var transformTemplate = (_c = this.options.visualElement) === null || _c === void 0 ? void 0 : _c.getProps().transformTemplate;
|
|
3184
3231
|
this.prevTransformTemplateValue = transformTemplate === null || transformTemplate === void 0 ? void 0 : transformTemplate(this.latestValues, "");
|
|
@@ -4558,6 +4605,10 @@
|
|
|
4558
4605
|
"whileFocus",
|
|
4559
4606
|
"whileTap",
|
|
4560
4607
|
"whileHover",
|
|
4608
|
+
"whileInView",
|
|
4609
|
+
"onViewportEnter",
|
|
4610
|
+
"onViewportLeave",
|
|
4611
|
+
"viewport",
|
|
4561
4612
|
"layoutScroll",
|
|
4562
4613
|
]);
|
|
4563
4614
|
/**
|
|
@@ -4912,6 +4963,7 @@
|
|
|
4912
4963
|
AnimationType["Tap"] = "whileTap";
|
|
4913
4964
|
AnimationType["Drag"] = "whileDrag";
|
|
4914
4965
|
AnimationType["Focus"] = "whileFocus";
|
|
4966
|
+
AnimationType["InView"] = "whileInView";
|
|
4915
4967
|
AnimationType["Exit"] = "exit";
|
|
4916
4968
|
})(AnimationType || (AnimationType = {}));
|
|
4917
4969
|
|
|
@@ -5221,12 +5273,163 @@
|
|
|
5221
5273
|
useUnmountEffect(removePointerEndListener);
|
|
5222
5274
|
}
|
|
5223
5275
|
|
|
5276
|
+
var warned = new Set();
|
|
5277
|
+
function warnOnce(condition, message, element) {
|
|
5278
|
+
if (condition || warned.has(message))
|
|
5279
|
+
return;
|
|
5280
|
+
console.warn(message);
|
|
5281
|
+
if (element)
|
|
5282
|
+
console.warn(element);
|
|
5283
|
+
warned.add(message);
|
|
5284
|
+
}
|
|
5285
|
+
|
|
5286
|
+
/**
|
|
5287
|
+
* Map an IntersectionHandler callback to an element. We only ever make one handler for one
|
|
5288
|
+
* element, so even though these handlers might all be triggered by different
|
|
5289
|
+
* observers, we can keep them in the same map.
|
|
5290
|
+
*/
|
|
5291
|
+
var observerCallbacks = new WeakMap();
|
|
5292
|
+
/**
|
|
5293
|
+
* Multiple observers can be created for multiple element/document roots. Each with
|
|
5294
|
+
* different settings. So here we store dictionaries of observers to each root,
|
|
5295
|
+
* using serialised settings (threshold/margin) as lookup keys.
|
|
5296
|
+
*/
|
|
5297
|
+
var observers = new WeakMap();
|
|
5298
|
+
var fireObserverCallback = function (entry) {
|
|
5299
|
+
var _a;
|
|
5300
|
+
(_a = observerCallbacks.get(entry.target)) === null || _a === void 0 ? void 0 : _a(entry);
|
|
5301
|
+
};
|
|
5302
|
+
var fireAllObserverCallbacks = function (entries) {
|
|
5303
|
+
entries.forEach(fireObserverCallback);
|
|
5304
|
+
};
|
|
5305
|
+
function initIntersectionObserver(_a) {
|
|
5306
|
+
var root = _a.root, options = __rest(_a, ["root"]);
|
|
5307
|
+
var lookupRoot = root || document;
|
|
5308
|
+
/**
|
|
5309
|
+
* If we don't have an observer lookup map for this root, create one.
|
|
5310
|
+
*/
|
|
5311
|
+
if (!observers.has(lookupRoot)) {
|
|
5312
|
+
observers.set(lookupRoot, {});
|
|
5313
|
+
}
|
|
5314
|
+
var rootObservers = observers.get(lookupRoot);
|
|
5315
|
+
var key = JSON.stringify(options);
|
|
5316
|
+
/**
|
|
5317
|
+
* If we don't have an observer for this combination of root and settings,
|
|
5318
|
+
* create one.
|
|
5319
|
+
*/
|
|
5320
|
+
if (!rootObservers[key]) {
|
|
5321
|
+
rootObservers[key] = new IntersectionObserver(fireAllObserverCallbacks, __assign({ root: root }, options));
|
|
5322
|
+
}
|
|
5323
|
+
return rootObservers[key];
|
|
5324
|
+
}
|
|
5325
|
+
function observeIntersection(element, options, callback) {
|
|
5326
|
+
var rootInteresectionObserver = initIntersectionObserver(options);
|
|
5327
|
+
observerCallbacks.set(element, callback);
|
|
5328
|
+
rootInteresectionObserver.observe(element);
|
|
5329
|
+
return function () {
|
|
5330
|
+
observerCallbacks.delete(element);
|
|
5331
|
+
rootInteresectionObserver.unobserve(element);
|
|
5332
|
+
};
|
|
5333
|
+
}
|
|
5334
|
+
|
|
5335
|
+
function useViewport(_a) {
|
|
5336
|
+
var visualElement = _a.visualElement, whileInView = _a.whileInView, onViewportEnter = _a.onViewportEnter, onViewportLeave = _a.onViewportLeave, _b = _a.viewport, viewport = _b === void 0 ? {} : _b;
|
|
5337
|
+
var state = React.useRef({
|
|
5338
|
+
hasEnteredView: false,
|
|
5339
|
+
isInView: false,
|
|
5340
|
+
});
|
|
5341
|
+
var shouldObserve = Boolean(whileInView || onViewportEnter || onViewportLeave);
|
|
5342
|
+
if (viewport.once && state.current.hasEnteredView)
|
|
5343
|
+
shouldObserve = false;
|
|
5344
|
+
var useObserver = typeof IntersectionObserver === "undefined"
|
|
5345
|
+
? useMissingIntersectionObserver
|
|
5346
|
+
: useIntersectionObserver;
|
|
5347
|
+
useObserver(shouldObserve, state.current, visualElement, viewport);
|
|
5348
|
+
}
|
|
5349
|
+
var thresholdNames = {
|
|
5350
|
+
some: 0,
|
|
5351
|
+
all: 1,
|
|
5352
|
+
};
|
|
5353
|
+
function useIntersectionObserver(shouldObserve, state, visualElement, _a) {
|
|
5354
|
+
var root = _a.root, rootMargin = _a.margin, _b = _a.amount, amount = _b === void 0 ? "some" : _b, once = _a.once;
|
|
5355
|
+
React.useEffect(function () {
|
|
5356
|
+
if (!shouldObserve)
|
|
5357
|
+
return;
|
|
5358
|
+
var options = {
|
|
5359
|
+
root: root === null || root === void 0 ? void 0 : root.current,
|
|
5360
|
+
rootMargin: rootMargin,
|
|
5361
|
+
threshold: typeof amount === "number" ? amount : thresholdNames[amount],
|
|
5362
|
+
};
|
|
5363
|
+
var intersectionCallback = function (entry) {
|
|
5364
|
+
var _a;
|
|
5365
|
+
var isIntersecting = entry.isIntersecting;
|
|
5366
|
+
/**
|
|
5367
|
+
* If there's been no change in the viewport state, early return.
|
|
5368
|
+
*/
|
|
5369
|
+
if (state.isInView === isIntersecting)
|
|
5370
|
+
return;
|
|
5371
|
+
state.isInView = isIntersecting;
|
|
5372
|
+
/**
|
|
5373
|
+
* Handle hasEnteredView. If this is only meant to run once, and
|
|
5374
|
+
* element isn't visible, early return. Otherwise set hasEnteredView to true.
|
|
5375
|
+
*/
|
|
5376
|
+
if (once && !isIntersecting && state.hasEnteredView) {
|
|
5377
|
+
return;
|
|
5378
|
+
}
|
|
5379
|
+
else if (isIntersecting) {
|
|
5380
|
+
state.hasEnteredView = true;
|
|
5381
|
+
}
|
|
5382
|
+
(_a = visualElement.animationState) === null || _a === void 0 ? void 0 : _a.setActive(AnimationType.InView, isIntersecting);
|
|
5383
|
+
/**
|
|
5384
|
+
* Use the latest committed props rather than the ones in scope
|
|
5385
|
+
* when this observer is created
|
|
5386
|
+
*/
|
|
5387
|
+
var props = visualElement.getProps();
|
|
5388
|
+
var callback = isIntersecting
|
|
5389
|
+
? props.onViewportEnter
|
|
5390
|
+
: props.onViewportLeave;
|
|
5391
|
+
callback === null || callback === void 0 ? void 0 : callback();
|
|
5392
|
+
};
|
|
5393
|
+
return observeIntersection(visualElement.getInstance(), options, intersectionCallback);
|
|
5394
|
+
}, [shouldObserve, root, rootMargin, amount]);
|
|
5395
|
+
}
|
|
5396
|
+
/**
|
|
5397
|
+
* If IntersectionObserver is missing, we activate inView and fire onViewportEnter
|
|
5398
|
+
* on mount. This way, the page will be in the state the author expects users
|
|
5399
|
+
* to see it in for everyone.
|
|
5400
|
+
*/
|
|
5401
|
+
function useMissingIntersectionObserver(shouldObserve, state, visualElement) {
|
|
5402
|
+
React.useEffect(function () {
|
|
5403
|
+
if (!shouldObserve)
|
|
5404
|
+
return;
|
|
5405
|
+
{
|
|
5406
|
+
warnOnce(false, "IntersectionObserver not available on this device. whileInView animations will trigger on mount.");
|
|
5407
|
+
}
|
|
5408
|
+
/**
|
|
5409
|
+
* Fire this in an rAF because, at this point, the animation state
|
|
5410
|
+
* won't have flushed for the first time and there's certain logic in
|
|
5411
|
+
* there that behaves differently on the initial animation.
|
|
5412
|
+
*
|
|
5413
|
+
* This hook should be quite rarely called so setting this in an rAF
|
|
5414
|
+
* is preferred to changing the behaviour of the animation state.
|
|
5415
|
+
*/
|
|
5416
|
+
requestAnimationFrame(function () {
|
|
5417
|
+
var _a;
|
|
5418
|
+
state.hasEnteredView = true;
|
|
5419
|
+
var onViewportEnter = visualElement.getProps().onViewportEnter;
|
|
5420
|
+
onViewportEnter === null || onViewportEnter === void 0 ? void 0 : onViewportEnter();
|
|
5421
|
+
(_a = visualElement.animationState) === null || _a === void 0 ? void 0 : _a.setActive(AnimationType.InView, true);
|
|
5422
|
+
});
|
|
5423
|
+
}, [shouldObserve]);
|
|
5424
|
+
}
|
|
5425
|
+
|
|
5224
5426
|
var makeRenderlessComponent = function (hook) { return function (props) {
|
|
5225
5427
|
hook(props);
|
|
5226
5428
|
return null;
|
|
5227
5429
|
}; };
|
|
5228
5430
|
|
|
5229
5431
|
var gestureAnimations = {
|
|
5432
|
+
inView: makeRenderlessComponent(useViewport),
|
|
5230
5433
|
tap: makeRenderlessComponent(useTapGesture),
|
|
5231
5434
|
focus: makeRenderlessComponent(useFocusGesture),
|
|
5232
5435
|
hover: makeRenderlessComponent(useHoverGesture),
|
|
@@ -5597,6 +5800,7 @@
|
|
|
5597
5800
|
|
|
5598
5801
|
var variantPriorityOrder = [
|
|
5599
5802
|
AnimationType.Animate,
|
|
5803
|
+
AnimationType.InView,
|
|
5600
5804
|
AnimationType.Focus,
|
|
5601
5805
|
AnimationType.Hover,
|
|
5602
5806
|
AnimationType.Tap,
|
|
@@ -5913,6 +6117,7 @@
|
|
|
5913
6117
|
var _a;
|
|
5914
6118
|
return _a = {},
|
|
5915
6119
|
_a[AnimationType.Animate] = createTypeState(true),
|
|
6120
|
+
_a[AnimationType.InView] = createTypeState(),
|
|
5916
6121
|
_a[AnimationType.Hover] = createTypeState(),
|
|
5917
6122
|
_a[AnimationType.Tap] = createTypeState(),
|
|
5918
6123
|
_a[AnimationType.Drag] = createTypeState(),
|
|
@@ -7422,11 +7627,18 @@
|
|
|
7422
7627
|
var element = visualElement.getInstance();
|
|
7423
7628
|
var elementComputedStyle = getComputedStyle(element);
|
|
7424
7629
|
var display = elementComputedStyle.display;
|
|
7630
|
+
var origin = {};
|
|
7425
7631
|
// If the element is currently set to display: "none", make it visible before
|
|
7426
7632
|
// measuring the target bounding box
|
|
7427
7633
|
if (display === "none") {
|
|
7428
7634
|
visualElement.setStaticValue("display", target.display || "block");
|
|
7429
7635
|
}
|
|
7636
|
+
/**
|
|
7637
|
+
* Record origins before we render and update styles
|
|
7638
|
+
*/
|
|
7639
|
+
changedKeys.forEach(function (key) {
|
|
7640
|
+
origin[key] = positionalValues[key](originBbox, elementComputedStyle);
|
|
7641
|
+
});
|
|
7430
7642
|
// Apply the latest values (as set in checkAndConvertChangedValueTypes)
|
|
7431
7643
|
visualElement.syncRender();
|
|
7432
7644
|
var targetBbox = visualElement.measureViewportBox();
|
|
@@ -7434,7 +7646,7 @@
|
|
|
7434
7646
|
// Restore styles to their **calculated computed style**, not their actual
|
|
7435
7647
|
// originally set style. This allows us to animate between equivalent pixel units.
|
|
7436
7648
|
var value = visualElement.getValue(key);
|
|
7437
|
-
setAndResetVelocity(value,
|
|
7649
|
+
setAndResetVelocity(value, origin[key]);
|
|
7438
7650
|
target[key] = positionalValues[key](targetBbox, elementComputedStyle);
|
|
7439
7651
|
});
|
|
7440
7652
|
return target;
|