motion 12.39.0 → 12.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/motion.dev.js +601 -357
- package/dist/motion.js +1 -1
- package/package.json +3 -3
package/dist/motion.dev.js
CHANGED
|
@@ -2161,8 +2161,15 @@
|
|
|
2161
2161
|
];
|
|
2162
2162
|
/**
|
|
2163
2163
|
* A quick lookup for transform props.
|
|
2164
|
+
*
|
|
2165
|
+
* `pathRotation` is a transform for routing purposes (skipped from raw
|
|
2166
|
+
* style application, wired to the transform composite, flags transform
|
|
2167
|
+
* dirty) but is intentionally NOT in `transformPropOrder` — it is
|
|
2168
|
+
* composed onto `rotate` at the build sites, not serialized in its own
|
|
2169
|
+
* slot, and must stay out of the order-array consumers (parse-transform,
|
|
2170
|
+
* unit-conversion, keys-position).
|
|
2164
2171
|
*/
|
|
2165
|
-
const transformProps = /*@__PURE__*/ (() => new Set(transformPropOrder))();
|
|
2172
|
+
const transformProps = /*@__PURE__*/ (() => new Set([...transformPropOrder, "pathRotation"]))();
|
|
2166
2173
|
|
|
2167
2174
|
const isNumOrPxType = (v) => v === number || v === px;
|
|
2168
2175
|
const transformKeys = new Set(["x", "y", "z"]);
|
|
@@ -3175,271 +3182,6 @@
|
|
|
3175
3182
|
: maxStaggerDuration - index * staggerChildren;
|
|
3176
3183
|
}
|
|
3177
3184
|
|
|
3178
|
-
/**
|
|
3179
|
-
* Parse Framer's special CSS variable format into a CSS token and a fallback.
|
|
3180
|
-
*
|
|
3181
|
-
* ```
|
|
3182
|
-
* `var(--foo, #fff)` => [`--foo`, '#fff']
|
|
3183
|
-
* ```
|
|
3184
|
-
*
|
|
3185
|
-
* @param current
|
|
3186
|
-
*/
|
|
3187
|
-
const splitCSSVariableRegex =
|
|
3188
|
-
// eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words
|
|
3189
|
-
/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;
|
|
3190
|
-
function parseCSSVariable(current) {
|
|
3191
|
-
const match = splitCSSVariableRegex.exec(current);
|
|
3192
|
-
if (!match)
|
|
3193
|
-
return [,];
|
|
3194
|
-
const [, token1, token2, fallback] = match;
|
|
3195
|
-
return [`--${token1 ?? token2}`, fallback];
|
|
3196
|
-
}
|
|
3197
|
-
const maxDepth = 4;
|
|
3198
|
-
function getVariableValue(current, element, depth = 1) {
|
|
3199
|
-
exports.invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`, "max-css-var-depth");
|
|
3200
|
-
const [token, fallback] = parseCSSVariable(current);
|
|
3201
|
-
// No CSS variable detected
|
|
3202
|
-
if (!token)
|
|
3203
|
-
return;
|
|
3204
|
-
// Attempt to read this CSS variable off the element
|
|
3205
|
-
const resolved = window.getComputedStyle(element).getPropertyValue(token);
|
|
3206
|
-
if (resolved) {
|
|
3207
|
-
const trimmed = resolved.trim();
|
|
3208
|
-
return isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed;
|
|
3209
|
-
}
|
|
3210
|
-
return isCSSVariableToken(fallback)
|
|
3211
|
-
? getVariableValue(fallback, element, depth + 1)
|
|
3212
|
-
: fallback;
|
|
3213
|
-
}
|
|
3214
|
-
|
|
3215
|
-
const underDampedSpring = {
|
|
3216
|
-
type: "spring",
|
|
3217
|
-
stiffness: 500,
|
|
3218
|
-
damping: 25,
|
|
3219
|
-
restSpeed: 10,
|
|
3220
|
-
};
|
|
3221
|
-
const criticallyDampedSpring = (target) => ({
|
|
3222
|
-
type: "spring",
|
|
3223
|
-
stiffness: 550,
|
|
3224
|
-
damping: target === 0 ? 2 * Math.sqrt(550) : 30,
|
|
3225
|
-
restSpeed: 10,
|
|
3226
|
-
});
|
|
3227
|
-
const keyframesTransition = {
|
|
3228
|
-
type: "keyframes",
|
|
3229
|
-
duration: 0.8,
|
|
3230
|
-
};
|
|
3231
|
-
/**
|
|
3232
|
-
* Default easing curve is a slightly shallower version of
|
|
3233
|
-
* the default browser easing curve.
|
|
3234
|
-
*/
|
|
3235
|
-
const ease = {
|
|
3236
|
-
type: "keyframes",
|
|
3237
|
-
ease: [0.25, 0.1, 0.35, 1],
|
|
3238
|
-
duration: 0.3,
|
|
3239
|
-
};
|
|
3240
|
-
const getDefaultTransition = (valueKey, { keyframes }) => {
|
|
3241
|
-
if (keyframes.length > 2) {
|
|
3242
|
-
return keyframesTransition;
|
|
3243
|
-
}
|
|
3244
|
-
else if (transformProps.has(valueKey)) {
|
|
3245
|
-
return valueKey.startsWith("scale")
|
|
3246
|
-
? criticallyDampedSpring(keyframes[1])
|
|
3247
|
-
: underDampedSpring;
|
|
3248
|
-
}
|
|
3249
|
-
return ease;
|
|
3250
|
-
};
|
|
3251
|
-
|
|
3252
|
-
/**
|
|
3253
|
-
* If `transition` has `inherit: true`, shallow-merge it with
|
|
3254
|
-
* `parentTransition` (child keys win) and strip the `inherit` key.
|
|
3255
|
-
* Otherwise return `transition` unchanged.
|
|
3256
|
-
*/
|
|
3257
|
-
function resolveTransition(transition, parentTransition) {
|
|
3258
|
-
if (transition?.inherit && parentTransition) {
|
|
3259
|
-
const { inherit: _, ...rest } = transition;
|
|
3260
|
-
return { ...parentTransition, ...rest };
|
|
3261
|
-
}
|
|
3262
|
-
return transition;
|
|
3263
|
-
}
|
|
3264
|
-
|
|
3265
|
-
function getValueTransition$1(transition, key) {
|
|
3266
|
-
const valueTransition = transition?.[key] ??
|
|
3267
|
-
transition?.["default"] ??
|
|
3268
|
-
transition;
|
|
3269
|
-
if (valueTransition !== transition) {
|
|
3270
|
-
return resolveTransition(valueTransition, transition);
|
|
3271
|
-
}
|
|
3272
|
-
return valueTransition;
|
|
3273
|
-
}
|
|
3274
|
-
|
|
3275
|
-
const orchestrationKeys = new Set([
|
|
3276
|
-
"when",
|
|
3277
|
-
"delay",
|
|
3278
|
-
"delayChildren",
|
|
3279
|
-
"staggerChildren",
|
|
3280
|
-
"staggerDirection",
|
|
3281
|
-
"repeat",
|
|
3282
|
-
"repeatType",
|
|
3283
|
-
"repeatDelay",
|
|
3284
|
-
"from",
|
|
3285
|
-
"elapsed",
|
|
3286
|
-
]);
|
|
3287
|
-
/**
|
|
3288
|
-
* Decide whether a transition is defined on a given Transition.
|
|
3289
|
-
* This filters out orchestration options and returns true
|
|
3290
|
-
* if any options are left.
|
|
3291
|
-
*/
|
|
3292
|
-
function isTransitionDefined(transition) {
|
|
3293
|
-
for (const key in transition) {
|
|
3294
|
-
if (!orchestrationKeys.has(key))
|
|
3295
|
-
return true;
|
|
3296
|
-
}
|
|
3297
|
-
return false;
|
|
3298
|
-
}
|
|
3299
|
-
|
|
3300
|
-
const animateMotionValue = (name, value, target, transition = {}, element, isHandoff) => (onComplete) => {
|
|
3301
|
-
const valueTransition = getValueTransition$1(transition, name) || {};
|
|
3302
|
-
/**
|
|
3303
|
-
* Most transition values are currently completely overwritten by value-specific
|
|
3304
|
-
* transitions. In the future it'd be nicer to blend these transitions. But for now
|
|
3305
|
-
* delay actually does inherit from the root transition if not value-specific.
|
|
3306
|
-
*/
|
|
3307
|
-
const delay = valueTransition.delay || transition.delay || 0;
|
|
3308
|
-
/**
|
|
3309
|
-
* Elapsed isn't a public transition option but can be passed through from
|
|
3310
|
-
* optimized appear effects in milliseconds.
|
|
3311
|
-
*/
|
|
3312
|
-
let { elapsed = 0 } = transition;
|
|
3313
|
-
elapsed = elapsed - secondsToMilliseconds(delay);
|
|
3314
|
-
const options = {
|
|
3315
|
-
keyframes: Array.isArray(target) ? target : [null, target],
|
|
3316
|
-
ease: "easeOut",
|
|
3317
|
-
velocity: value.getVelocity(),
|
|
3318
|
-
...valueTransition,
|
|
3319
|
-
delay: -elapsed,
|
|
3320
|
-
onUpdate: (v) => {
|
|
3321
|
-
value.set(v);
|
|
3322
|
-
valueTransition.onUpdate && valueTransition.onUpdate(v);
|
|
3323
|
-
},
|
|
3324
|
-
onComplete: () => {
|
|
3325
|
-
onComplete();
|
|
3326
|
-
valueTransition.onComplete && valueTransition.onComplete();
|
|
3327
|
-
},
|
|
3328
|
-
name,
|
|
3329
|
-
motionValue: value,
|
|
3330
|
-
element: isHandoff ? undefined : element,
|
|
3331
|
-
};
|
|
3332
|
-
/**
|
|
3333
|
-
* If there's no transition defined for this value, we can generate
|
|
3334
|
-
* unique transition settings for this value.
|
|
3335
|
-
*/
|
|
3336
|
-
if (!isTransitionDefined(valueTransition)) {
|
|
3337
|
-
Object.assign(options, getDefaultTransition(name, options));
|
|
3338
|
-
}
|
|
3339
|
-
/**
|
|
3340
|
-
* Both WAAPI and our internal animation functions use durations
|
|
3341
|
-
* as defined by milliseconds, while our external API defines them
|
|
3342
|
-
* as seconds.
|
|
3343
|
-
*/
|
|
3344
|
-
options.duration && (options.duration = secondsToMilliseconds(options.duration));
|
|
3345
|
-
options.repeatDelay && (options.repeatDelay = secondsToMilliseconds(options.repeatDelay));
|
|
3346
|
-
/**
|
|
3347
|
-
* Support deprecated way to set initial value. Prefer keyframe syntax.
|
|
3348
|
-
*/
|
|
3349
|
-
if (options.from !== undefined) {
|
|
3350
|
-
options.keyframes[0] = options.from;
|
|
3351
|
-
}
|
|
3352
|
-
let shouldSkip = false;
|
|
3353
|
-
if (options.type === false ||
|
|
3354
|
-
(options.duration === 0 && !options.repeatDelay)) {
|
|
3355
|
-
makeAnimationInstant(options);
|
|
3356
|
-
if (options.delay === 0) {
|
|
3357
|
-
shouldSkip = true;
|
|
3358
|
-
}
|
|
3359
|
-
}
|
|
3360
|
-
if (MotionGlobalConfig.instantAnimations ||
|
|
3361
|
-
MotionGlobalConfig.skipAnimations ||
|
|
3362
|
-
element?.shouldSkipAnimations ||
|
|
3363
|
-
valueTransition.skipAnimations) {
|
|
3364
|
-
shouldSkip = true;
|
|
3365
|
-
makeAnimationInstant(options);
|
|
3366
|
-
options.delay = 0;
|
|
3367
|
-
}
|
|
3368
|
-
/**
|
|
3369
|
-
* If the transition type or easing has been explicitly set by the user
|
|
3370
|
-
* then we don't want to allow flattening the animation.
|
|
3371
|
-
*/
|
|
3372
|
-
options.allowFlatten = !valueTransition.type && !valueTransition.ease;
|
|
3373
|
-
/**
|
|
3374
|
-
* If we can or must skip creating the animation, and apply only
|
|
3375
|
-
* the final keyframe, do so. We also check once keyframes are resolved but
|
|
3376
|
-
* this early check prevents the need to create an animation at all.
|
|
3377
|
-
*/
|
|
3378
|
-
if (shouldSkip && !isHandoff && value.get() !== undefined) {
|
|
3379
|
-
const finalKeyframe = getFinalKeyframe(options.keyframes, valueTransition);
|
|
3380
|
-
if (finalKeyframe !== undefined) {
|
|
3381
|
-
frame.update(() => {
|
|
3382
|
-
options.onUpdate(finalKeyframe);
|
|
3383
|
-
options.onComplete();
|
|
3384
|
-
});
|
|
3385
|
-
return;
|
|
3386
|
-
}
|
|
3387
|
-
}
|
|
3388
|
-
return valueTransition.isSync
|
|
3389
|
-
? new JSAnimation(options)
|
|
3390
|
-
: new AsyncMotionValueAnimation(options);
|
|
3391
|
-
};
|
|
3392
|
-
|
|
3393
|
-
function getValueState(visualElement) {
|
|
3394
|
-
const state = [{}, {}];
|
|
3395
|
-
visualElement?.values.forEach((value, key) => {
|
|
3396
|
-
state[0][key] = value.get();
|
|
3397
|
-
state[1][key] = value.getVelocity();
|
|
3398
|
-
});
|
|
3399
|
-
return state;
|
|
3400
|
-
}
|
|
3401
|
-
function resolveVariantFromProps(props, definition, custom, visualElement) {
|
|
3402
|
-
/**
|
|
3403
|
-
* If the variant definition is a function, resolve.
|
|
3404
|
-
*/
|
|
3405
|
-
if (typeof definition === "function") {
|
|
3406
|
-
const [current, velocity] = getValueState(visualElement);
|
|
3407
|
-
definition = definition(custom !== undefined ? custom : props.custom, current, velocity);
|
|
3408
|
-
}
|
|
3409
|
-
/**
|
|
3410
|
-
* If the variant definition is a variant label, or
|
|
3411
|
-
* the function returned a variant label, resolve.
|
|
3412
|
-
*/
|
|
3413
|
-
if (typeof definition === "string") {
|
|
3414
|
-
definition = props.variants && props.variants[definition];
|
|
3415
|
-
}
|
|
3416
|
-
/**
|
|
3417
|
-
* At this point we've resolved both functions and variant labels,
|
|
3418
|
-
* but the resolved variant label might itself have been a function.
|
|
3419
|
-
* If so, resolve. This can only have returned a valid target object.
|
|
3420
|
-
*/
|
|
3421
|
-
if (typeof definition === "function") {
|
|
3422
|
-
const [current, velocity] = getValueState(visualElement);
|
|
3423
|
-
definition = definition(custom !== undefined ? custom : props.custom, current, velocity);
|
|
3424
|
-
}
|
|
3425
|
-
return definition;
|
|
3426
|
-
}
|
|
3427
|
-
|
|
3428
|
-
function resolveVariant(visualElement, definition, custom) {
|
|
3429
|
-
const props = visualElement.getProps();
|
|
3430
|
-
return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement);
|
|
3431
|
-
}
|
|
3432
|
-
|
|
3433
|
-
const positionalKeys = new Set([
|
|
3434
|
-
"width",
|
|
3435
|
-
"height",
|
|
3436
|
-
"top",
|
|
3437
|
-
"left",
|
|
3438
|
-
"right",
|
|
3439
|
-
"bottom",
|
|
3440
|
-
...transformPropOrder,
|
|
3441
|
-
]);
|
|
3442
|
-
|
|
3443
3185
|
/**
|
|
3444
3186
|
* Maximum time between the value of two frames, beyond which we
|
|
3445
3187
|
* assume the velocity has since been 0.
|
|
@@ -3648,116 +3390,562 @@
|
|
|
3648
3390
|
}
|
|
3649
3391
|
}
|
|
3650
3392
|
/**
|
|
3651
|
-
* Returns the latest state of `MotionValue`
|
|
3652
|
-
*
|
|
3653
|
-
* @returns - The latest state of `MotionValue`
|
|
3654
|
-
*
|
|
3655
|
-
* @public
|
|
3393
|
+
* Returns the latest state of `MotionValue`
|
|
3394
|
+
*
|
|
3395
|
+
* @returns - The latest state of `MotionValue`
|
|
3396
|
+
*
|
|
3397
|
+
* @public
|
|
3398
|
+
*/
|
|
3399
|
+
get() {
|
|
3400
|
+
if (collectMotionValues.current) {
|
|
3401
|
+
collectMotionValues.current.push(this);
|
|
3402
|
+
}
|
|
3403
|
+
return this.current;
|
|
3404
|
+
}
|
|
3405
|
+
/**
|
|
3406
|
+
* @public
|
|
3407
|
+
*/
|
|
3408
|
+
getPrevious() {
|
|
3409
|
+
return this.prev;
|
|
3410
|
+
}
|
|
3411
|
+
/**
|
|
3412
|
+
* Returns the latest velocity of `MotionValue`
|
|
3413
|
+
*
|
|
3414
|
+
* @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical.
|
|
3415
|
+
*
|
|
3416
|
+
* @public
|
|
3417
|
+
*/
|
|
3418
|
+
getVelocity() {
|
|
3419
|
+
const currentTime = time.now();
|
|
3420
|
+
if (!this.canTrackVelocity ||
|
|
3421
|
+
this.prevFrameValue === undefined ||
|
|
3422
|
+
currentTime - this.updatedAt > MAX_VELOCITY_DELTA) {
|
|
3423
|
+
return 0;
|
|
3424
|
+
}
|
|
3425
|
+
const delta = Math.min(this.updatedAt - this.prevUpdatedAt, MAX_VELOCITY_DELTA);
|
|
3426
|
+
// Casts because of parseFloat's poor typing
|
|
3427
|
+
return velocityPerSecond(parseFloat(this.current) -
|
|
3428
|
+
parseFloat(this.prevFrameValue), delta);
|
|
3429
|
+
}
|
|
3430
|
+
/**
|
|
3431
|
+
* Registers a new animation to control this `MotionValue`. Only one
|
|
3432
|
+
* animation can drive a `MotionValue` at one time.
|
|
3433
|
+
*
|
|
3434
|
+
* ```jsx
|
|
3435
|
+
* value.start()
|
|
3436
|
+
* ```
|
|
3437
|
+
*
|
|
3438
|
+
* @param animation - A function that starts the provided animation
|
|
3439
|
+
*/
|
|
3440
|
+
start(startAnimation) {
|
|
3441
|
+
this.stop();
|
|
3442
|
+
return new Promise((resolve) => {
|
|
3443
|
+
this.hasAnimated = true;
|
|
3444
|
+
this.animation = startAnimation(resolve);
|
|
3445
|
+
if (this.events.animationStart) {
|
|
3446
|
+
this.events.animationStart.notify();
|
|
3447
|
+
}
|
|
3448
|
+
}).then(() => {
|
|
3449
|
+
if (this.events.animationComplete) {
|
|
3450
|
+
this.events.animationComplete.notify();
|
|
3451
|
+
}
|
|
3452
|
+
this.clearAnimation();
|
|
3453
|
+
});
|
|
3454
|
+
}
|
|
3455
|
+
/**
|
|
3456
|
+
* Stop the currently active animation.
|
|
3457
|
+
*
|
|
3458
|
+
* @public
|
|
3459
|
+
*/
|
|
3460
|
+
stop() {
|
|
3461
|
+
if (this.animation) {
|
|
3462
|
+
this.animation.stop();
|
|
3463
|
+
if (this.events.animationCancel) {
|
|
3464
|
+
this.events.animationCancel.notify();
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
this.clearAnimation();
|
|
3468
|
+
}
|
|
3469
|
+
/**
|
|
3470
|
+
* Returns `true` if this value is currently animating.
|
|
3471
|
+
*
|
|
3472
|
+
* @public
|
|
3473
|
+
*/
|
|
3474
|
+
isAnimating() {
|
|
3475
|
+
return !!this.animation;
|
|
3476
|
+
}
|
|
3477
|
+
clearAnimation() {
|
|
3478
|
+
delete this.animation;
|
|
3479
|
+
}
|
|
3480
|
+
/**
|
|
3481
|
+
* Destroy and clean up subscribers to this `MotionValue`.
|
|
3482
|
+
*
|
|
3483
|
+
* The `MotionValue` hooks like `useMotionValue` and `useTransform` automatically
|
|
3484
|
+
* handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually
|
|
3485
|
+
* created a `MotionValue` via the `motionValue` function.
|
|
3486
|
+
*
|
|
3487
|
+
* @public
|
|
3488
|
+
*/
|
|
3489
|
+
destroy() {
|
|
3490
|
+
this.dependents?.clear();
|
|
3491
|
+
this.events.destroy?.notify();
|
|
3492
|
+
this.clearListeners();
|
|
3493
|
+
this.stop();
|
|
3494
|
+
if (this.stopPassiveEffect) {
|
|
3495
|
+
this.stopPassiveEffect();
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
function motionValue(init, options) {
|
|
3500
|
+
return new MotionValue(init, options);
|
|
3501
|
+
}
|
|
3502
|
+
|
|
3503
|
+
/**
|
|
3504
|
+
* If `transition` has `inherit: true`, shallow-merge it with
|
|
3505
|
+
* `parentTransition` (child keys win) and strip the `inherit` key.
|
|
3506
|
+
* Otherwise return `transition` unchanged.
|
|
3507
|
+
*/
|
|
3508
|
+
function resolveTransition(transition, parentTransition) {
|
|
3509
|
+
if (transition?.inherit && parentTransition) {
|
|
3510
|
+
const { inherit: _, ...rest } = transition;
|
|
3511
|
+
return { ...parentTransition, ...rest };
|
|
3512
|
+
}
|
|
3513
|
+
return transition;
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3516
|
+
function getValueTransition$1(transition, key) {
|
|
3517
|
+
const valueTransition = transition?.[key] ??
|
|
3518
|
+
transition?.["default"] ??
|
|
3519
|
+
transition;
|
|
3520
|
+
if (valueTransition !== transition) {
|
|
3521
|
+
return resolveTransition(valueTransition, transition);
|
|
3522
|
+
}
|
|
3523
|
+
return valueTransition;
|
|
3524
|
+
}
|
|
3525
|
+
|
|
3526
|
+
const underDampedSpring = {
|
|
3527
|
+
type: "spring",
|
|
3528
|
+
stiffness: 500,
|
|
3529
|
+
damping: 25,
|
|
3530
|
+
restSpeed: 10,
|
|
3531
|
+
};
|
|
3532
|
+
const criticallyDampedSpring = (target) => ({
|
|
3533
|
+
type: "spring",
|
|
3534
|
+
stiffness: 550,
|
|
3535
|
+
damping: target === 0 ? 2 * Math.sqrt(550) : 30,
|
|
3536
|
+
restSpeed: 10,
|
|
3537
|
+
});
|
|
3538
|
+
const keyframesTransition = {
|
|
3539
|
+
type: "keyframes",
|
|
3540
|
+
duration: 0.8,
|
|
3541
|
+
};
|
|
3542
|
+
/**
|
|
3543
|
+
* Default easing curve is a slightly shallower version of
|
|
3544
|
+
* the default browser easing curve.
|
|
3545
|
+
*/
|
|
3546
|
+
const ease = {
|
|
3547
|
+
type: "keyframes",
|
|
3548
|
+
ease: [0.25, 0.1, 0.35, 1],
|
|
3549
|
+
duration: 0.3,
|
|
3550
|
+
};
|
|
3551
|
+
const getDefaultTransition = (valueKey, { keyframes }) => {
|
|
3552
|
+
if (keyframes.length > 2) {
|
|
3553
|
+
return keyframesTransition;
|
|
3554
|
+
}
|
|
3555
|
+
else if (transformProps.has(valueKey)) {
|
|
3556
|
+
return valueKey.startsWith("scale")
|
|
3557
|
+
? criticallyDampedSpring(keyframes[1])
|
|
3558
|
+
: underDampedSpring;
|
|
3559
|
+
}
|
|
3560
|
+
return ease;
|
|
3561
|
+
};
|
|
3562
|
+
|
|
3563
|
+
const orchestrationKeys = new Set([
|
|
3564
|
+
"when",
|
|
3565
|
+
"delay",
|
|
3566
|
+
"delayChildren",
|
|
3567
|
+
"staggerChildren",
|
|
3568
|
+
"staggerDirection",
|
|
3569
|
+
"repeat",
|
|
3570
|
+
"repeatType",
|
|
3571
|
+
"repeatDelay",
|
|
3572
|
+
"from",
|
|
3573
|
+
"elapsed",
|
|
3574
|
+
]);
|
|
3575
|
+
/**
|
|
3576
|
+
* Decide whether a transition is defined on a given Transition.
|
|
3577
|
+
* This filters out orchestration options and returns true
|
|
3578
|
+
* if any options are left.
|
|
3579
|
+
*/
|
|
3580
|
+
function isTransitionDefined(transition) {
|
|
3581
|
+
for (const key in transition) {
|
|
3582
|
+
if (!orchestrationKeys.has(key))
|
|
3583
|
+
return true;
|
|
3584
|
+
}
|
|
3585
|
+
return false;
|
|
3586
|
+
}
|
|
3587
|
+
|
|
3588
|
+
const animateMotionValue = (name, value, target, transition = {}, element, isHandoff) => (onComplete) => {
|
|
3589
|
+
const valueTransition = getValueTransition$1(transition, name) || {};
|
|
3590
|
+
/**
|
|
3591
|
+
* Most transition values are currently completely overwritten by value-specific
|
|
3592
|
+
* transitions. In the future it'd be nicer to blend these transitions. But for now
|
|
3593
|
+
* delay actually does inherit from the root transition if not value-specific.
|
|
3594
|
+
*/
|
|
3595
|
+
const delay = valueTransition.delay || transition.delay || 0;
|
|
3596
|
+
/**
|
|
3597
|
+
* Elapsed isn't a public transition option but can be passed through from
|
|
3598
|
+
* optimized appear effects in milliseconds.
|
|
3599
|
+
*/
|
|
3600
|
+
let { elapsed = 0 } = transition;
|
|
3601
|
+
elapsed = elapsed - secondsToMilliseconds(delay);
|
|
3602
|
+
const options = {
|
|
3603
|
+
keyframes: Array.isArray(target) ? target : [null, target],
|
|
3604
|
+
ease: "easeOut",
|
|
3605
|
+
velocity: value.getVelocity(),
|
|
3606
|
+
...valueTransition,
|
|
3607
|
+
delay: -elapsed,
|
|
3608
|
+
onUpdate: (v) => {
|
|
3609
|
+
value.set(v);
|
|
3610
|
+
valueTransition.onUpdate && valueTransition.onUpdate(v);
|
|
3611
|
+
},
|
|
3612
|
+
onComplete: () => {
|
|
3613
|
+
onComplete();
|
|
3614
|
+
valueTransition.onComplete && valueTransition.onComplete();
|
|
3615
|
+
},
|
|
3616
|
+
name,
|
|
3617
|
+
motionValue: value,
|
|
3618
|
+
element: isHandoff ? undefined : element,
|
|
3619
|
+
};
|
|
3620
|
+
/**
|
|
3621
|
+
* If there's no transition defined for this value, we can generate
|
|
3622
|
+
* unique transition settings for this value.
|
|
3656
3623
|
*/
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
collectMotionValues.current.push(this);
|
|
3660
|
-
}
|
|
3661
|
-
return this.current;
|
|
3624
|
+
if (!isTransitionDefined(valueTransition)) {
|
|
3625
|
+
Object.assign(options, getDefaultTransition(name, options));
|
|
3662
3626
|
}
|
|
3663
3627
|
/**
|
|
3664
|
-
*
|
|
3628
|
+
* Both WAAPI and our internal animation functions use durations
|
|
3629
|
+
* as defined by milliseconds, while our external API defines them
|
|
3630
|
+
* as seconds.
|
|
3665
3631
|
*/
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
}
|
|
3632
|
+
options.duration && (options.duration = secondsToMilliseconds(options.duration));
|
|
3633
|
+
options.repeatDelay && (options.repeatDelay = secondsToMilliseconds(options.repeatDelay));
|
|
3669
3634
|
/**
|
|
3670
|
-
*
|
|
3671
|
-
*
|
|
3672
|
-
* @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical.
|
|
3673
|
-
*
|
|
3674
|
-
* @public
|
|
3635
|
+
* Support deprecated way to set initial value. Prefer keyframe syntax.
|
|
3675
3636
|
*/
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3637
|
+
if (options.from !== undefined) {
|
|
3638
|
+
options.keyframes[0] = options.from;
|
|
3639
|
+
}
|
|
3640
|
+
let shouldSkip = false;
|
|
3641
|
+
if (options.type === false ||
|
|
3642
|
+
(options.duration === 0 && !options.repeatDelay)) {
|
|
3643
|
+
makeAnimationInstant(options);
|
|
3644
|
+
if (options.delay === 0) {
|
|
3645
|
+
shouldSkip = true;
|
|
3682
3646
|
}
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3647
|
+
}
|
|
3648
|
+
if (MotionGlobalConfig.instantAnimations ||
|
|
3649
|
+
MotionGlobalConfig.skipAnimations ||
|
|
3650
|
+
element?.shouldSkipAnimations ||
|
|
3651
|
+
valueTransition.skipAnimations) {
|
|
3652
|
+
shouldSkip = true;
|
|
3653
|
+
makeAnimationInstant(options);
|
|
3654
|
+
options.delay = 0;
|
|
3687
3655
|
}
|
|
3688
3656
|
/**
|
|
3689
|
-
*
|
|
3690
|
-
*
|
|
3691
|
-
*
|
|
3692
|
-
* ```jsx
|
|
3693
|
-
* value.start()
|
|
3694
|
-
* ```
|
|
3695
|
-
*
|
|
3696
|
-
* @param animation - A function that starts the provided animation
|
|
3657
|
+
* If the transition type or easing has been explicitly set by the user
|
|
3658
|
+
* then we don't want to allow flattening the animation.
|
|
3697
3659
|
*/
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3660
|
+
options.allowFlatten = !valueTransition.type && !valueTransition.ease;
|
|
3661
|
+
/**
|
|
3662
|
+
* If we can or must skip creating the animation, and apply only
|
|
3663
|
+
* the final keyframe, do so. We also check once keyframes are resolved but
|
|
3664
|
+
* this early check prevents the need to create an animation at all.
|
|
3665
|
+
*/
|
|
3666
|
+
if (shouldSkip && !isHandoff && value.get() !== undefined) {
|
|
3667
|
+
const finalKeyframe = getFinalKeyframe(options.keyframes, valueTransition);
|
|
3668
|
+
if (finalKeyframe !== undefined) {
|
|
3669
|
+
frame.update(() => {
|
|
3670
|
+
options.onUpdate(finalKeyframe);
|
|
3671
|
+
options.onComplete();
|
|
3672
|
+
});
|
|
3673
|
+
return;
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
return valueTransition.isSync
|
|
3677
|
+
? new JSAnimation(options)
|
|
3678
|
+
: new AsyncMotionValueAnimation(options);
|
|
3679
|
+
};
|
|
3680
|
+
|
|
3681
|
+
const MIN_LAYOUT_DISTANCE = 20;
|
|
3682
|
+
function bezierPoint(t, origin, control, target) {
|
|
3683
|
+
const inv = 1 - t;
|
|
3684
|
+
return inv * inv * origin + 2 * inv * t * control + t * t * target;
|
|
3685
|
+
}
|
|
3686
|
+
function bezierTangentAngle(t, originX, controlX, targetX, originY, controlY, targetY) {
|
|
3687
|
+
const dx = 2 * (1 - t) * (controlX - originX) + 2 * t * (targetX - controlX);
|
|
3688
|
+
const dy = 2 * (1 - t) * (controlY - originY) + 2 * t * (targetY - controlY);
|
|
3689
|
+
return Math.atan2(dy, dx) * (180 / Math.PI);
|
|
3690
|
+
}
|
|
3691
|
+
function computeArcControlPoint(fromX, fromY, toX, toY, strength, peak) {
|
|
3692
|
+
const deltaX = toX - fromX;
|
|
3693
|
+
const deltaY = toY - fromY;
|
|
3694
|
+
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
3695
|
+
if (distance > 0) {
|
|
3696
|
+
const normalPerpX = -deltaY / distance;
|
|
3697
|
+
const normalPerpY = deltaX / distance;
|
|
3698
|
+
const desiredHeight = strength * distance;
|
|
3699
|
+
return {
|
|
3700
|
+
x: fromX + deltaX * peak + normalPerpX * desiredHeight,
|
|
3701
|
+
y: fromY + deltaY * peak + normalPerpY * desiredHeight,
|
|
3702
|
+
};
|
|
3703
|
+
}
|
|
3704
|
+
return { x: fromX, y: fromY };
|
|
3705
|
+
}
|
|
3706
|
+
/**
|
|
3707
|
+
* The pure sampling factory: `(from, to) => (t) => point`. Internal —
|
|
3708
|
+
* used by {@link arc} and the unit tests. Not part of the public surface.
|
|
3709
|
+
*/
|
|
3710
|
+
function createArcPath({ strength = 0.5, peak = 0.5, direction, rotate = false, } = {}) {
|
|
3711
|
+
const rotationScale = rotate === true ? 1 : typeof rotate === "number" ? rotate : 0;
|
|
3712
|
+
// Auto-direction only: persists across calls to flip the bulge back
|
|
3713
|
+
// onto the same screen side when the dominant axis changes between
|
|
3714
|
+
// calls. Reuse the factory (module scope / useMemo) to keep this alive.
|
|
3715
|
+
let prevBulgeSign;
|
|
3716
|
+
const createInterpolator = (from, to) => {
|
|
3717
|
+
const dx = to.x - from.x;
|
|
3718
|
+
const dy = to.y - from.y;
|
|
3719
|
+
let signed;
|
|
3720
|
+
if (direction === "cw") {
|
|
3721
|
+
signed = -strength;
|
|
3722
|
+
}
|
|
3723
|
+
else if (direction === "ccw") {
|
|
3724
|
+
signed = strength;
|
|
3725
|
+
}
|
|
3726
|
+
else {
|
|
3727
|
+
const dom = Math.abs(dx) >= Math.abs(dy) ? dx : dy;
|
|
3728
|
+
signed = dom < 0 ? -strength : strength;
|
|
3729
|
+
}
|
|
3730
|
+
let control = computeArcControlPoint(from.x, from.y, to.x, to.y, signed, peak);
|
|
3731
|
+
if (direction === undefined) {
|
|
3732
|
+
const isVertical = Math.abs(dx) < Math.abs(dy);
|
|
3733
|
+
const midX = from.x + dx * peak;
|
|
3734
|
+
const midY = from.y + dy * peak;
|
|
3735
|
+
const bulgeSign = isVertical
|
|
3736
|
+
? Math.sign(control.x - midX)
|
|
3737
|
+
: Math.sign(control.y - midY);
|
|
3738
|
+
if (prevBulgeSign !== undefined &&
|
|
3739
|
+
bulgeSign !== 0 &&
|
|
3740
|
+
bulgeSign !== prevBulgeSign) {
|
|
3741
|
+
signed = -signed;
|
|
3742
|
+
control = computeArcControlPoint(from.x, from.y, to.x, to.y, signed, peak);
|
|
3743
|
+
}
|
|
3744
|
+
else if (bulgeSign !== 0) {
|
|
3745
|
+
prevBulgeSign = bulgeSign;
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
const tangent0 = rotationScale
|
|
3749
|
+
? bezierTangentAngle(0, from.x, control.x, to.x, from.y, control.y, to.y)
|
|
3750
|
+
: 0;
|
|
3751
|
+
const tangent1 = rotationScale
|
|
3752
|
+
? bezierTangentAngle(1, from.x, control.x, to.x, from.y, control.y, to.y)
|
|
3753
|
+
: 0;
|
|
3754
|
+
const tangentDelta = rotationScale
|
|
3755
|
+
? wrap(-180, 180, tangent1 - tangent0)
|
|
3756
|
+
: 0;
|
|
3757
|
+
return (t) => {
|
|
3758
|
+
const out = {
|
|
3759
|
+
x: bezierPoint(t, from.x, control.x, to.x),
|
|
3760
|
+
y: bezierPoint(t, from.y, control.y, to.y),
|
|
3761
|
+
};
|
|
3762
|
+
if (rotationScale) {
|
|
3763
|
+
const raw = bezierTangentAngle(t, from.x, control.x, to.x, from.y, control.y, to.y);
|
|
3764
|
+
const baseline = tangent0 + tangentDelta * t;
|
|
3765
|
+
out.rotate = wrap(-180, 180, raw - baseline) * rotationScale;
|
|
3705
3766
|
}
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3767
|
+
return out;
|
|
3768
|
+
};
|
|
3769
|
+
};
|
|
3770
|
+
return createInterpolator;
|
|
3771
|
+
}
|
|
3772
|
+
/**
|
|
3773
|
+
* Creates a curved path for `transition.path`:
|
|
3774
|
+
*
|
|
3775
|
+
* ```ts
|
|
3776
|
+
* <motion.div animate={{ x: 200, y: 100 }} transition={{ path: arc() }} />
|
|
3777
|
+
* ```
|
|
3778
|
+
*
|
|
3779
|
+
* Reuse the returned value (module scope / useMemo / useRef) so its
|
|
3780
|
+
* continuity closure survives re-renders — a fresh `arc()` has no memory.
|
|
3781
|
+
*/
|
|
3782
|
+
function arc(options = {}) {
|
|
3783
|
+
const sample = createArcPath(options);
|
|
3784
|
+
const path = {
|
|
3785
|
+
interpolateProjection(delta) {
|
|
3786
|
+
// `from` is the current translate offset (carries any in-flight
|
|
3787
|
+
// displacement when interrupted); `to` is the new layout origin.
|
|
3788
|
+
// The distance floor avoids visible wobble on tiny shifts.
|
|
3789
|
+
const tx = delta.x.translate;
|
|
3790
|
+
const ty = delta.y.translate;
|
|
3791
|
+
if (Math.sqrt(tx * tx + ty * ty) < MIN_LAYOUT_DISTANCE) {
|
|
3792
|
+
return undefined;
|
|
3709
3793
|
}
|
|
3710
|
-
|
|
3711
|
-
}
|
|
3794
|
+
return sample({ x: tx, y: ty }, { x: 0, y: 0 });
|
|
3795
|
+
},
|
|
3796
|
+
animateVisualElement(visualElement, target, transition, delay, animations) {
|
|
3797
|
+
if (!("x" in target || "y" in target))
|
|
3798
|
+
return;
|
|
3799
|
+
const xValue = visualElement.getValue("x", visualElement.latestValues["x"] ?? 0);
|
|
3800
|
+
const yValue = visualElement.getValue("y", visualElement.latestValues["y"] ?? 0);
|
|
3801
|
+
const xRaw = target.x;
|
|
3802
|
+
const yRaw = target.y;
|
|
3803
|
+
const xFrom = (Array.isArray(xRaw) && xRaw[0] != null
|
|
3804
|
+
? xRaw[0]
|
|
3805
|
+
: xValue?.get()) ?? 0;
|
|
3806
|
+
const yFrom = (Array.isArray(yRaw) && yRaw[0] != null
|
|
3807
|
+
? yRaw[0]
|
|
3808
|
+
: yValue?.get()) ?? 0;
|
|
3809
|
+
const xTo = (Array.isArray(xRaw)
|
|
3810
|
+
? xRaw[xRaw.length - 1]
|
|
3811
|
+
: xRaw ?? xFrom);
|
|
3812
|
+
const yTo = (Array.isArray(yRaw)
|
|
3813
|
+
? yRaw[yRaw.length - 1]
|
|
3814
|
+
: yRaw ?? yFrom);
|
|
3815
|
+
// Interruption needs no flag: x/y already hold the displaced
|
|
3816
|
+
// mid-arc position, so xFrom/yFrom carry the continuity geometry.
|
|
3817
|
+
const interpolate = sample({ x: xFrom, y: yFrom }, { x: xTo, y: yTo });
|
|
3818
|
+
// Drive a dedicated `pathRotation` value (composed onto `rotate`
|
|
3819
|
+
// at the build sites) rather than `rotate` itself, so a
|
|
3820
|
+
// concurrent rotate animation composes and nothing accumulates
|
|
3821
|
+
// on interrupt.
|
|
3822
|
+
const pathRotationValue = interpolate(0).rotate !== undefined
|
|
3823
|
+
? visualElement.getValue("pathRotation", 0)
|
|
3824
|
+
: undefined;
|
|
3825
|
+
const pathTransition = {
|
|
3826
|
+
delay,
|
|
3827
|
+
...getValueTransition$1(transition || {}, "x"),
|
|
3828
|
+
};
|
|
3829
|
+
delete pathTransition.path;
|
|
3830
|
+
const progress = motionValue(0);
|
|
3831
|
+
progress.start(animateMotionValue("", progress, [0, 1000], {
|
|
3832
|
+
...pathTransition,
|
|
3833
|
+
isSync: true,
|
|
3834
|
+
velocity: 0,
|
|
3835
|
+
onUpdate: (latest) => {
|
|
3836
|
+
const point = interpolate(latest / 1000);
|
|
3837
|
+
xValue?.set(point.x);
|
|
3838
|
+
yValue?.set(point.y);
|
|
3839
|
+
if (pathRotationValue && point.rotate !== undefined) {
|
|
3840
|
+
pathRotationValue.set(point.rotate);
|
|
3841
|
+
}
|
|
3842
|
+
},
|
|
3843
|
+
onComplete: () => {
|
|
3844
|
+
xValue?.set(xTo);
|
|
3845
|
+
yValue?.set(yTo);
|
|
3846
|
+
pathRotationValue?.set(0);
|
|
3847
|
+
},
|
|
3848
|
+
// Interrupt/cancel must clear our additive contribution
|
|
3849
|
+
// so it can't linger on top of the user's `rotate`.
|
|
3850
|
+
onStop: () => pathRotationValue?.set(0),
|
|
3851
|
+
onCancel: () => pathRotationValue?.set(0),
|
|
3852
|
+
}));
|
|
3853
|
+
if (progress.animation)
|
|
3854
|
+
animations.push(progress.animation);
|
|
3855
|
+
delete target.x;
|
|
3856
|
+
delete target.y;
|
|
3857
|
+
},
|
|
3858
|
+
};
|
|
3859
|
+
return path;
|
|
3860
|
+
}
|
|
3861
|
+
|
|
3862
|
+
/**
|
|
3863
|
+
* Parse Framer's special CSS variable format into a CSS token and a fallback.
|
|
3864
|
+
*
|
|
3865
|
+
* ```
|
|
3866
|
+
* `var(--foo, #fff)` => [`--foo`, '#fff']
|
|
3867
|
+
* ```
|
|
3868
|
+
*
|
|
3869
|
+
* @param current
|
|
3870
|
+
*/
|
|
3871
|
+
const splitCSSVariableRegex =
|
|
3872
|
+
// eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words
|
|
3873
|
+
/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;
|
|
3874
|
+
function parseCSSVariable(current) {
|
|
3875
|
+
const match = splitCSSVariableRegex.exec(current);
|
|
3876
|
+
if (!match)
|
|
3877
|
+
return [,];
|
|
3878
|
+
const [, token1, token2, fallback] = match;
|
|
3879
|
+
return [`--${token1 ?? token2}`, fallback];
|
|
3880
|
+
}
|
|
3881
|
+
const maxDepth = 4;
|
|
3882
|
+
function getVariableValue(current, element, depth = 1) {
|
|
3883
|
+
exports.invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`, "max-css-var-depth");
|
|
3884
|
+
const [token, fallback] = parseCSSVariable(current);
|
|
3885
|
+
// No CSS variable detected
|
|
3886
|
+
if (!token)
|
|
3887
|
+
return;
|
|
3888
|
+
// Attempt to read this CSS variable off the element
|
|
3889
|
+
const resolved = window.getComputedStyle(element).getPropertyValue(token);
|
|
3890
|
+
if (resolved) {
|
|
3891
|
+
const trimmed = resolved.trim();
|
|
3892
|
+
return isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed;
|
|
3712
3893
|
}
|
|
3894
|
+
return isCSSVariableToken(fallback)
|
|
3895
|
+
? getVariableValue(fallback, element, depth + 1)
|
|
3896
|
+
: fallback;
|
|
3897
|
+
}
|
|
3898
|
+
|
|
3899
|
+
function getValueState(visualElement) {
|
|
3900
|
+
const state = [{}, {}];
|
|
3901
|
+
visualElement?.values.forEach((value, key) => {
|
|
3902
|
+
state[0][key] = value.get();
|
|
3903
|
+
state[1][key] = value.getVelocity();
|
|
3904
|
+
});
|
|
3905
|
+
return state;
|
|
3906
|
+
}
|
|
3907
|
+
function resolveVariantFromProps(props, definition, custom, visualElement) {
|
|
3713
3908
|
/**
|
|
3714
|
-
*
|
|
3715
|
-
*
|
|
3716
|
-
* @public
|
|
3909
|
+
* If the variant definition is a function, resolve.
|
|
3717
3910
|
*/
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
if (this.events.animationCancel) {
|
|
3722
|
-
this.events.animationCancel.notify();
|
|
3723
|
-
}
|
|
3724
|
-
}
|
|
3725
|
-
this.clearAnimation();
|
|
3911
|
+
if (typeof definition === "function") {
|
|
3912
|
+
const [current, velocity] = getValueState(visualElement);
|
|
3913
|
+
definition = definition(custom !== undefined ? custom : props.custom, current, velocity);
|
|
3726
3914
|
}
|
|
3727
3915
|
/**
|
|
3728
|
-
*
|
|
3729
|
-
*
|
|
3730
|
-
* @public
|
|
3916
|
+
* If the variant definition is a variant label, or
|
|
3917
|
+
* the function returned a variant label, resolve.
|
|
3731
3918
|
*/
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
}
|
|
3735
|
-
clearAnimation() {
|
|
3736
|
-
delete this.animation;
|
|
3919
|
+
if (typeof definition === "string") {
|
|
3920
|
+
definition = props.variants && props.variants[definition];
|
|
3737
3921
|
}
|
|
3738
3922
|
/**
|
|
3739
|
-
*
|
|
3740
|
-
*
|
|
3741
|
-
*
|
|
3742
|
-
* handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually
|
|
3743
|
-
* created a `MotionValue` via the `motionValue` function.
|
|
3744
|
-
*
|
|
3745
|
-
* @public
|
|
3923
|
+
* At this point we've resolved both functions and variant labels,
|
|
3924
|
+
* but the resolved variant label might itself have been a function.
|
|
3925
|
+
* If so, resolve. This can only have returned a valid target object.
|
|
3746
3926
|
*/
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
this.clearListeners();
|
|
3751
|
-
this.stop();
|
|
3752
|
-
if (this.stopPassiveEffect) {
|
|
3753
|
-
this.stopPassiveEffect();
|
|
3754
|
-
}
|
|
3927
|
+
if (typeof definition === "function") {
|
|
3928
|
+
const [current, velocity] = getValueState(visualElement);
|
|
3929
|
+
definition = definition(custom !== undefined ? custom : props.custom, current, velocity);
|
|
3755
3930
|
}
|
|
3931
|
+
return definition;
|
|
3756
3932
|
}
|
|
3757
|
-
|
|
3758
|
-
|
|
3933
|
+
|
|
3934
|
+
function resolveVariant(visualElement, definition, custom) {
|
|
3935
|
+
const props = visualElement.getProps();
|
|
3936
|
+
return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement);
|
|
3759
3937
|
}
|
|
3760
3938
|
|
|
3939
|
+
const positionalKeys = new Set([
|
|
3940
|
+
"width",
|
|
3941
|
+
"height",
|
|
3942
|
+
"top",
|
|
3943
|
+
"left",
|
|
3944
|
+
"right",
|
|
3945
|
+
"bottom",
|
|
3946
|
+
...transformPropOrder,
|
|
3947
|
+
]);
|
|
3948
|
+
|
|
3761
3949
|
const isKeyframesTarget = (v) => {
|
|
3762
3950
|
return Array.isArray(v);
|
|
3763
3951
|
};
|
|
@@ -3846,6 +4034,11 @@
|
|
|
3846
4034
|
const animationTypeState = type &&
|
|
3847
4035
|
visualElement.animationState &&
|
|
3848
4036
|
visualElement.animationState.getState()[type];
|
|
4037
|
+
const path = transition?.path;
|
|
4038
|
+
if (path) {
|
|
4039
|
+
// path mutates `target` to claim x/y; loop below skips them.
|
|
4040
|
+
path.animateVisualElement(visualElement, target, transition, delay, animations);
|
|
4041
|
+
}
|
|
3849
4042
|
for (const key in target) {
|
|
3850
4043
|
const value = visualElement.getValue(key, visualElement.latestValues[key] ?? null);
|
|
3851
4044
|
const valueTarget = target[key];
|
|
@@ -4064,6 +4257,12 @@
|
|
|
4064
4257
|
|
|
4065
4258
|
const transformValueTypes = {
|
|
4066
4259
|
rotate: degrees,
|
|
4260
|
+
/**
|
|
4261
|
+
* Internal channel for `transition.path` orientToPath. Composed onto
|
|
4262
|
+
* `rotate` at the transform-build sites so the user's `rotate` is
|
|
4263
|
+
* never read or overwritten. Not part of `transformPropOrder`.
|
|
4264
|
+
*/
|
|
4265
|
+
pathRotation: degrees,
|
|
4067
4266
|
rotateX: degrees,
|
|
4068
4267
|
rotateY: degrees,
|
|
4069
4268
|
rotateZ: degrees,
|
|
@@ -4615,6 +4814,15 @@
|
|
|
4615
4814
|
transform += `${transformName}(${value}) `;
|
|
4616
4815
|
}
|
|
4617
4816
|
}
|
|
4817
|
+
// See build-transform.ts: additive `rotate()` so user `rotate` isn't
|
|
4818
|
+
// clobbered. Not a `transformPropOrder` slot.
|
|
4819
|
+
const pathRotation = state.latest.pathRotation;
|
|
4820
|
+
if (pathRotation) {
|
|
4821
|
+
transformIsDefault = false;
|
|
4822
|
+
transform += `rotate(${typeof pathRotation === "number"
|
|
4823
|
+
? `${pathRotation}deg`
|
|
4824
|
+
: pathRotation}) `;
|
|
4825
|
+
}
|
|
4618
4826
|
return transformIsDefault ? "none" : transform.trim();
|
|
4619
4827
|
}
|
|
4620
4828
|
|
|
@@ -6767,6 +6975,14 @@
|
|
|
6767
6975
|
}
|
|
6768
6976
|
}
|
|
6769
6977
|
}
|
|
6978
|
+
// `pathRotation` composes onto `rotate` as a separate additive term so
|
|
6979
|
+
// the user's `rotate` is never clobbered. Deliberately not a slot in
|
|
6980
|
+
// `transformPropOrder`.
|
|
6981
|
+
const pathRotation = latestValues.pathRotation;
|
|
6982
|
+
if (pathRotation) {
|
|
6983
|
+
transformIsDefault = false;
|
|
6984
|
+
transformString += `rotate(${getValueAsType(pathRotation, numberValueTypes.pathRotation)}) `;
|
|
6985
|
+
}
|
|
6770
6986
|
transformString = transformString.trim();
|
|
6771
6987
|
// If we have a custom `transform` template, pass our transform values and
|
|
6772
6988
|
// generated transformString to that before returning
|
|
@@ -7829,11 +8045,14 @@
|
|
|
7829
8045
|
transform += `scale(${1 / treeScale.x}, ${1 / treeScale.y}) `;
|
|
7830
8046
|
}
|
|
7831
8047
|
if (latestTransform) {
|
|
7832
|
-
const { transformPerspective, rotate, rotateX, rotateY, skewX, skewY } = latestTransform;
|
|
8048
|
+
const { transformPerspective, rotate, pathRotation, rotateX, rotateY, skewX, skewY, } = latestTransform;
|
|
7833
8049
|
if (transformPerspective)
|
|
7834
8050
|
transform = `perspective(${transformPerspective}px) ${transform}`;
|
|
7835
8051
|
if (rotate)
|
|
7836
8052
|
transform += `rotate(${rotate}deg) `;
|
|
8053
|
+
// Additive `rotate()` so user `rotate` isn't clobbered.
|
|
8054
|
+
if (pathRotation)
|
|
8055
|
+
transform += `rotate(${pathRotation}deg) `;
|
|
7837
8056
|
if (rotateX)
|
|
7838
8057
|
transform += `rotateX(${rotateX}deg) `;
|
|
7839
8058
|
if (rotateY)
|
|
@@ -8388,7 +8607,8 @@
|
|
|
8388
8607
|
* Set animation origin after starting animation to avoid layout jump
|
|
8389
8608
|
* caused by stopping previous layout animation
|
|
8390
8609
|
*/
|
|
8391
|
-
this.setAnimationOrigin(delta, hasOnlyRelativeTargetChanged
|
|
8610
|
+
this.setAnimationOrigin(delta, hasOnlyRelativeTargetChanged, animationOptions
|
|
8611
|
+
.path);
|
|
8392
8612
|
}
|
|
8393
8613
|
else {
|
|
8394
8614
|
/**
|
|
@@ -9104,7 +9324,7 @@
|
|
|
9104
9324
|
this.projectionDelta = createDelta();
|
|
9105
9325
|
this.projectionDeltaWithTransform = createDelta();
|
|
9106
9326
|
}
|
|
9107
|
-
setAnimationOrigin(delta, hasOnlyRelativeTargetChanged = false) {
|
|
9327
|
+
setAnimationOrigin(delta, hasOnlyRelativeTargetChanged = false, pathFn) {
|
|
9108
9328
|
const snapshot = this.snapshot;
|
|
9109
9329
|
const snapshotLatestValues = snapshot ? snapshot.latestValues : {};
|
|
9110
9330
|
const mixedValues = { ...this.latestValues };
|
|
@@ -9126,10 +9346,26 @@
|
|
|
9126
9346
|
!this.path.some(hasOpacityCrossfade));
|
|
9127
9347
|
this.animationProgress = 0;
|
|
9128
9348
|
let prevRelativeTarget;
|
|
9349
|
+
// The path decides whether the layout shift is worth curving
|
|
9350
|
+
// (distance floor) and resolves the interpolator from the delta.
|
|
9351
|
+
const interpolate = pathFn?.interpolateProjection(delta);
|
|
9129
9352
|
this.mixTargetDelta = (latest) => {
|
|
9130
9353
|
const progress = latest / 1000;
|
|
9131
|
-
|
|
9132
|
-
|
|
9354
|
+
const point = interpolate?.(progress);
|
|
9355
|
+
if (point) {
|
|
9356
|
+
targetDelta.x.translate = point.x;
|
|
9357
|
+
targetDelta.x.scale = mixNumber$1(delta.x.scale, 1, progress);
|
|
9358
|
+
targetDelta.x.origin = delta.x.origin;
|
|
9359
|
+
targetDelta.x.originPoint = delta.x.originPoint;
|
|
9360
|
+
targetDelta.y.translate = point.y;
|
|
9361
|
+
targetDelta.y.scale = mixNumber$1(delta.y.scale, 1, progress);
|
|
9362
|
+
targetDelta.y.origin = delta.y.origin;
|
|
9363
|
+
targetDelta.y.originPoint = delta.y.originPoint;
|
|
9364
|
+
}
|
|
9365
|
+
else {
|
|
9366
|
+
mixAxisDeltaLinear(targetDelta.x, delta.x, progress);
|
|
9367
|
+
mixAxisDeltaLinear(targetDelta.y, delta.y, progress);
|
|
9368
|
+
}
|
|
9133
9369
|
this.setTargetDelta(targetDelta);
|
|
9134
9370
|
if (this.relativeTarget &&
|
|
9135
9371
|
this.relativeTargetOrigin &&
|
|
@@ -9154,6 +9390,13 @@
|
|
|
9154
9390
|
this.animationValues = mixedValues;
|
|
9155
9391
|
mixValues(mixedValues, snapshotLatestValues, this.latestValues, progress, shouldCrossfadeOpacity, isOnlyMember);
|
|
9156
9392
|
}
|
|
9393
|
+
if (point && point.rotate !== undefined) {
|
|
9394
|
+
// Dedicated `pathRotation` channel, not `rotate`, so an
|
|
9395
|
+
// animating `rotate` is composed with, never clobbered.
|
|
9396
|
+
if (!this.animationValues)
|
|
9397
|
+
this.animationValues = mixedValues;
|
|
9398
|
+
this.animationValues.pathRotation = point.rotate;
|
|
9399
|
+
}
|
|
9157
9400
|
this.root.scheduleUpdateProjection();
|
|
9158
9401
|
this.scheduleRender();
|
|
9159
9402
|
this.animationProgress = progress;
|
|
@@ -9674,7 +9917,7 @@
|
|
|
9674
9917
|
function removeLeadSnapshots(stack) {
|
|
9675
9918
|
stack.removeLeadSnapshot();
|
|
9676
9919
|
}
|
|
9677
|
-
function
|
|
9920
|
+
function mixAxisDeltaLinear(output, delta, p) {
|
|
9678
9921
|
output.translate = mixNumber$1(delta.translate, 0, p);
|
|
9679
9922
|
output.scale = mixNumber$1(delta.scale, 1, p);
|
|
9680
9923
|
output.origin = delta.origin;
|
|
@@ -11426,6 +11669,7 @@
|
|
|
11426
11669
|
exports.applyPointDelta = applyPointDelta;
|
|
11427
11670
|
exports.applyPxDefaults = applyPxDefaults;
|
|
11428
11671
|
exports.applyTreeDeltas = applyTreeDeltas;
|
|
11672
|
+
exports.arc = arc;
|
|
11429
11673
|
exports.aspectRatio = aspectRatio;
|
|
11430
11674
|
exports.attachFollow = attachFollow;
|
|
11431
11675
|
exports.attachSpring = attachSpring;
|