first commit
This commit is contained in:
21
node_modules/motion-dom/LICENSE.md
generated
vendored
Normal file
21
node_modules/motion-dom/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2024 [Motion](https://motion.dev) B.V.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
4807
node_modules/motion-dom/dist/cjs/index.js
generated
vendored
Normal file
4807
node_modules/motion-dom/dist/cjs/index.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
178
node_modules/motion-dom/dist/es/animation/AsyncMotionValueAnimation.mjs
generated
vendored
Normal file
178
node_modules/motion-dom/dist/es/animation/AsyncMotionValueAnimation.mjs
generated
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
import { MotionGlobalConfig, noop } from 'motion-utils';
|
||||
import { time } from '../frameloop/sync-time.mjs';
|
||||
import { JSAnimation } from './JSAnimation.mjs';
|
||||
import { getFinalKeyframe } from './keyframes/get-final.mjs';
|
||||
import { KeyframeResolver, flushKeyframeResolvers } from './keyframes/KeyframesResolver.mjs';
|
||||
import { NativeAnimationExtended } from './NativeAnimationExtended.mjs';
|
||||
import { canAnimate } from './utils/can-animate.mjs';
|
||||
import { makeAnimationInstant } from './utils/make-animation-instant.mjs';
|
||||
import { WithPromise } from './utils/WithPromise.mjs';
|
||||
import { supportsBrowserAnimation } from './waapi/supports/waapi.mjs';
|
||||
|
||||
/**
|
||||
* Maximum time allowed between an animation being created and it being
|
||||
* resolved for us to use the latter as the start time.
|
||||
*
|
||||
* This is to ensure that while we prefer to "start" an animation as soon
|
||||
* as it's triggered, we also want to avoid a visual jump if there's a big delay
|
||||
* between these two moments.
|
||||
*/
|
||||
const MAX_RESOLVE_DELAY = 40;
|
||||
class AsyncMotionValueAnimation extends WithPromise {
|
||||
constructor({ autoplay = true, delay = 0, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", keyframes, name, motionValue, element, ...options }) {
|
||||
super();
|
||||
/**
|
||||
* Bound to support return animation.stop pattern
|
||||
*/
|
||||
this.stop = () => {
|
||||
if (this._animation) {
|
||||
this._animation.stop();
|
||||
this.stopTimeline?.();
|
||||
}
|
||||
this.keyframeResolver?.cancel();
|
||||
};
|
||||
this.createdAt = time.now();
|
||||
const optionsWithDefaults = {
|
||||
autoplay,
|
||||
delay,
|
||||
type,
|
||||
repeat,
|
||||
repeatDelay,
|
||||
repeatType,
|
||||
name,
|
||||
motionValue,
|
||||
element,
|
||||
...options,
|
||||
};
|
||||
const KeyframeResolver$1 = element?.KeyframeResolver || KeyframeResolver;
|
||||
this.keyframeResolver = new KeyframeResolver$1(keyframes, (resolvedKeyframes, finalKeyframe, forced) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe, optionsWithDefaults, !forced), name, motionValue, element);
|
||||
this.keyframeResolver?.scheduleResolve();
|
||||
}
|
||||
onKeyframesResolved(keyframes, finalKeyframe, options, sync) {
|
||||
this.keyframeResolver = undefined;
|
||||
const { name, type, velocity, delay, isHandoff, onUpdate } = options;
|
||||
this.resolvedAt = time.now();
|
||||
/**
|
||||
* If we can't animate this value with the resolved keyframes
|
||||
* then we should complete it immediately.
|
||||
*/
|
||||
if (!canAnimate(keyframes, name, type, velocity)) {
|
||||
if (MotionGlobalConfig.instantAnimations || !delay) {
|
||||
onUpdate?.(getFinalKeyframe(keyframes, options, finalKeyframe));
|
||||
}
|
||||
keyframes[0] = keyframes[keyframes.length - 1];
|
||||
makeAnimationInstant(options);
|
||||
options.repeat = 0;
|
||||
}
|
||||
/**
|
||||
* Resolve startTime for the animation.
|
||||
*
|
||||
* This method uses the createdAt and resolvedAt to calculate the
|
||||
* animation startTime. *Ideally*, we would use the createdAt time as t=0
|
||||
* as the following frame would then be the first frame of the animation in
|
||||
* progress, which would feel snappier.
|
||||
*
|
||||
* However, if there's a delay (main thread work) between the creation of
|
||||
* the animation and the first commited frame, we prefer to use resolvedAt
|
||||
* to avoid a sudden jump into the animation.
|
||||
*/
|
||||
const startTime = sync
|
||||
? !this.resolvedAt
|
||||
? this.createdAt
|
||||
: this.resolvedAt - this.createdAt > MAX_RESOLVE_DELAY
|
||||
? this.resolvedAt
|
||||
: this.createdAt
|
||||
: undefined;
|
||||
const resolvedOptions = {
|
||||
startTime,
|
||||
finalKeyframe,
|
||||
...options,
|
||||
keyframes,
|
||||
};
|
||||
/**
|
||||
* Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via
|
||||
* WAAPI. Therefore, this animation must be JS to ensure it runs "under" the
|
||||
* optimised animation.
|
||||
*/
|
||||
const animation = !isHandoff && supportsBrowserAnimation(resolvedOptions)
|
||||
? new NativeAnimationExtended({
|
||||
...resolvedOptions,
|
||||
element: resolvedOptions.motionValue.owner.current,
|
||||
})
|
||||
: new JSAnimation(resolvedOptions);
|
||||
animation.finished.then(() => this.notifyFinished()).catch(noop);
|
||||
if (this.pendingTimeline) {
|
||||
this.stopTimeline = animation.attachTimeline(this.pendingTimeline);
|
||||
this.pendingTimeline = undefined;
|
||||
}
|
||||
this._animation = animation;
|
||||
}
|
||||
get finished() {
|
||||
if (!this._animation) {
|
||||
return this._finished;
|
||||
}
|
||||
else {
|
||||
return this.animation.finished;
|
||||
}
|
||||
}
|
||||
then(onResolve, _onReject) {
|
||||
return this.finished.finally(onResolve).then(() => { });
|
||||
}
|
||||
get animation() {
|
||||
if (!this._animation) {
|
||||
this.keyframeResolver?.resume();
|
||||
flushKeyframeResolvers();
|
||||
}
|
||||
return this._animation;
|
||||
}
|
||||
get duration() {
|
||||
return this.animation.duration;
|
||||
}
|
||||
get iterationDuration() {
|
||||
return this.animation.iterationDuration;
|
||||
}
|
||||
get time() {
|
||||
return this.animation.time;
|
||||
}
|
||||
set time(newTime) {
|
||||
this.animation.time = newTime;
|
||||
}
|
||||
get speed() {
|
||||
return this.animation.speed;
|
||||
}
|
||||
get state() {
|
||||
return this.animation.state;
|
||||
}
|
||||
set speed(newSpeed) {
|
||||
this.animation.speed = newSpeed;
|
||||
}
|
||||
get startTime() {
|
||||
return this.animation.startTime;
|
||||
}
|
||||
attachTimeline(timeline) {
|
||||
if (this._animation) {
|
||||
this.stopTimeline = this.animation.attachTimeline(timeline);
|
||||
}
|
||||
else {
|
||||
this.pendingTimeline = timeline;
|
||||
}
|
||||
return () => this.stop();
|
||||
}
|
||||
play() {
|
||||
this.animation.play();
|
||||
}
|
||||
pause() {
|
||||
this.animation.pause();
|
||||
}
|
||||
complete() {
|
||||
this.animation.complete();
|
||||
}
|
||||
cancel() {
|
||||
if (this._animation) {
|
||||
this.animation.cancel();
|
||||
}
|
||||
this.keyframeResolver?.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
export { AsyncMotionValueAnimation };
|
||||
81
node_modules/motion-dom/dist/es/animation/GroupAnimation.mjs
generated
vendored
Normal file
81
node_modules/motion-dom/dist/es/animation/GroupAnimation.mjs
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
class GroupAnimation {
|
||||
constructor(animations) {
|
||||
// Bound to accomadate common `return animation.stop` pattern
|
||||
this.stop = () => this.runAll("stop");
|
||||
this.animations = animations.filter(Boolean);
|
||||
}
|
||||
get finished() {
|
||||
return Promise.all(this.animations.map((animation) => animation.finished));
|
||||
}
|
||||
/**
|
||||
* TODO: Filter out cancelled or stopped animations before returning
|
||||
*/
|
||||
getAll(propName) {
|
||||
return this.animations[0][propName];
|
||||
}
|
||||
setAll(propName, newValue) {
|
||||
for (let i = 0; i < this.animations.length; i++) {
|
||||
this.animations[i][propName] = newValue;
|
||||
}
|
||||
}
|
||||
attachTimeline(timeline) {
|
||||
const subscriptions = this.animations.map((animation) => animation.attachTimeline(timeline));
|
||||
return () => {
|
||||
subscriptions.forEach((cancel, i) => {
|
||||
cancel && cancel();
|
||||
this.animations[i].stop();
|
||||
});
|
||||
};
|
||||
}
|
||||
get time() {
|
||||
return this.getAll("time");
|
||||
}
|
||||
set time(time) {
|
||||
this.setAll("time", time);
|
||||
}
|
||||
get speed() {
|
||||
return this.getAll("speed");
|
||||
}
|
||||
set speed(speed) {
|
||||
this.setAll("speed", speed);
|
||||
}
|
||||
get state() {
|
||||
return this.getAll("state");
|
||||
}
|
||||
get startTime() {
|
||||
return this.getAll("startTime");
|
||||
}
|
||||
get duration() {
|
||||
return getMax(this.animations, "duration");
|
||||
}
|
||||
get iterationDuration() {
|
||||
return getMax(this.animations, "iterationDuration");
|
||||
}
|
||||
runAll(methodName) {
|
||||
this.animations.forEach((controls) => controls[methodName]());
|
||||
}
|
||||
play() {
|
||||
this.runAll("play");
|
||||
}
|
||||
pause() {
|
||||
this.runAll("pause");
|
||||
}
|
||||
cancel() {
|
||||
this.runAll("cancel");
|
||||
}
|
||||
complete() {
|
||||
this.runAll("complete");
|
||||
}
|
||||
}
|
||||
function getMax(animations, propName) {
|
||||
let max = 0;
|
||||
for (let i = 0; i < animations.length; i++) {
|
||||
const value = animations[i][propName];
|
||||
if (value !== null && value > max) {
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
export { GroupAnimation };
|
||||
9
node_modules/motion-dom/dist/es/animation/GroupAnimationWithThen.mjs
generated
vendored
Normal file
9
node_modules/motion-dom/dist/es/animation/GroupAnimationWithThen.mjs
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { GroupAnimation } from './GroupAnimation.mjs';
|
||||
|
||||
class GroupAnimationWithThen extends GroupAnimation {
|
||||
then(onResolve, _onReject) {
|
||||
return this.finished.finally(onResolve).then(() => { });
|
||||
}
|
||||
}
|
||||
|
||||
export { GroupAnimationWithThen };
|
||||
349
node_modules/motion-dom/dist/es/animation/JSAnimation.mjs
generated
vendored
Normal file
349
node_modules/motion-dom/dist/es/animation/JSAnimation.mjs
generated
vendored
Normal file
@@ -0,0 +1,349 @@
|
||||
import { invariant, pipe, clamp, millisecondsToSeconds, secondsToMilliseconds } from 'motion-utils';
|
||||
import { time } from '../frameloop/sync-time.mjs';
|
||||
import { activeAnimations } from '../stats/animation-count.mjs';
|
||||
import { mix } from '../utils/mix/index.mjs';
|
||||
import { frameloopDriver } from './drivers/frame.mjs';
|
||||
import { inertia } from './generators/inertia.mjs';
|
||||
import { keyframes } from './generators/keyframes.mjs';
|
||||
import { calcGeneratorDuration } from './generators/utils/calc-duration.mjs';
|
||||
import { getFinalKeyframe } from './keyframes/get-final.mjs';
|
||||
import { replaceTransitionType } from './utils/replace-transition-type.mjs';
|
||||
import { WithPromise } from './utils/WithPromise.mjs';
|
||||
|
||||
const percentToProgress = (percent) => percent / 100;
|
||||
class JSAnimation extends WithPromise {
|
||||
constructor(options) {
|
||||
super();
|
||||
this.state = "idle";
|
||||
this.startTime = null;
|
||||
this.isStopped = false;
|
||||
/**
|
||||
* The current time of the animation.
|
||||
*/
|
||||
this.currentTime = 0;
|
||||
/**
|
||||
* The time at which the animation was paused.
|
||||
*/
|
||||
this.holdTime = null;
|
||||
/**
|
||||
* Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed.
|
||||
*/
|
||||
this.playbackSpeed = 1;
|
||||
/**
|
||||
* This method is bound to the instance to fix a pattern where
|
||||
* animation.stop is returned as a reference from a useEffect.
|
||||
*/
|
||||
this.stop = () => {
|
||||
const { motionValue } = this.options;
|
||||
if (motionValue && motionValue.updatedAt !== time.now()) {
|
||||
this.tick(time.now());
|
||||
}
|
||||
this.isStopped = true;
|
||||
if (this.state === "idle")
|
||||
return;
|
||||
this.teardown();
|
||||
this.options.onStop?.();
|
||||
};
|
||||
activeAnimations.mainThread++;
|
||||
this.options = options;
|
||||
this.initAnimation();
|
||||
this.play();
|
||||
if (options.autoplay === false)
|
||||
this.pause();
|
||||
}
|
||||
initAnimation() {
|
||||
const { options } = this;
|
||||
replaceTransitionType(options);
|
||||
const { type = keyframes, repeat = 0, repeatDelay = 0, repeatType, velocity = 0, } = options;
|
||||
let { keyframes: keyframes$1 } = options;
|
||||
const generatorFactory = type || keyframes;
|
||||
if (process.env.NODE_ENV !== "production" &&
|
||||
generatorFactory !== keyframes) {
|
||||
invariant(keyframes$1.length <= 2, `Only two keyframes currently supported with spring and inertia animations. Trying to animate ${keyframes$1}`, "spring-two-frames");
|
||||
}
|
||||
if (generatorFactory !== keyframes &&
|
||||
typeof keyframes$1[0] !== "number") {
|
||||
this.mixKeyframes = pipe(percentToProgress, mix(keyframes$1[0], keyframes$1[1]));
|
||||
keyframes$1 = [0, 100];
|
||||
}
|
||||
const generator = generatorFactory({ ...options, keyframes: keyframes$1 });
|
||||
/**
|
||||
* If we have a mirror repeat type we need to create a second generator that outputs the
|
||||
* mirrored (not reversed) animation and later ping pong between the two generators.
|
||||
*/
|
||||
if (repeatType === "mirror") {
|
||||
this.mirroredGenerator = generatorFactory({
|
||||
...options,
|
||||
keyframes: [...keyframes$1].reverse(),
|
||||
velocity: -velocity,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* If duration is undefined and we have repeat options,
|
||||
* we need to calculate a duration from the generator.
|
||||
*
|
||||
* We set it to the generator itself to cache the duration.
|
||||
* Any timeline resolver will need to have already precalculated
|
||||
* the duration by this step.
|
||||
*/
|
||||
if (generator.calculatedDuration === null) {
|
||||
generator.calculatedDuration = calcGeneratorDuration(generator);
|
||||
}
|
||||
const { calculatedDuration } = generator;
|
||||
this.calculatedDuration = calculatedDuration;
|
||||
this.resolvedDuration = calculatedDuration + repeatDelay;
|
||||
this.totalDuration = this.resolvedDuration * (repeat + 1) - repeatDelay;
|
||||
this.generator = generator;
|
||||
}
|
||||
updateTime(timestamp) {
|
||||
const animationTime = Math.round(timestamp - this.startTime) * this.playbackSpeed;
|
||||
// Update currentTime
|
||||
if (this.holdTime !== null) {
|
||||
this.currentTime = this.holdTime;
|
||||
}
|
||||
else {
|
||||
// Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 =
|
||||
// 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for
|
||||
// example.
|
||||
this.currentTime = animationTime;
|
||||
}
|
||||
}
|
||||
tick(timestamp, sample = false) {
|
||||
const { generator, totalDuration, mixKeyframes, mirroredGenerator, resolvedDuration, calculatedDuration, } = this;
|
||||
if (this.startTime === null)
|
||||
return generator.next(0);
|
||||
const { delay = 0, keyframes, repeat, repeatType, repeatDelay, type, onUpdate, finalKeyframe, } = this.options;
|
||||
/**
|
||||
* requestAnimationFrame timestamps can come through as lower than
|
||||
* the startTime as set by performance.now(). Here we prevent this,
|
||||
* though in the future it could be possible to make setting startTime
|
||||
* a pending operation that gets resolved here.
|
||||
*/
|
||||
if (this.speed > 0) {
|
||||
this.startTime = Math.min(this.startTime, timestamp);
|
||||
}
|
||||
else if (this.speed < 0) {
|
||||
this.startTime = Math.min(timestamp - totalDuration / this.speed, this.startTime);
|
||||
}
|
||||
if (sample) {
|
||||
this.currentTime = timestamp;
|
||||
}
|
||||
else {
|
||||
this.updateTime(timestamp);
|
||||
}
|
||||
// Rebase on delay
|
||||
const timeWithoutDelay = this.currentTime - delay * (this.playbackSpeed >= 0 ? 1 : -1);
|
||||
const isInDelayPhase = this.playbackSpeed >= 0
|
||||
? timeWithoutDelay < 0
|
||||
: timeWithoutDelay > totalDuration;
|
||||
this.currentTime = Math.max(timeWithoutDelay, 0);
|
||||
// If this animation has finished, set the current time to the total duration.
|
||||
if (this.state === "finished" && this.holdTime === null) {
|
||||
this.currentTime = totalDuration;
|
||||
}
|
||||
let elapsed = this.currentTime;
|
||||
let frameGenerator = generator;
|
||||
if (repeat) {
|
||||
/**
|
||||
* Get the current progress (0-1) of the animation. If t is >
|
||||
* than duration we'll get values like 2.5 (midway through the
|
||||
* third iteration)
|
||||
*/
|
||||
const progress = Math.min(this.currentTime, totalDuration) / resolvedDuration;
|
||||
/**
|
||||
* Get the current iteration (0 indexed). For instance the floor of
|
||||
* 2.5 is 2.
|
||||
*/
|
||||
let currentIteration = Math.floor(progress);
|
||||
/**
|
||||
* Get the current progress of the iteration by taking the remainder
|
||||
* so 2.5 is 0.5 through iteration 2
|
||||
*/
|
||||
let iterationProgress = progress % 1.0;
|
||||
/**
|
||||
* If iteration progress is 1 we count that as the end
|
||||
* of the previous iteration.
|
||||
*/
|
||||
if (!iterationProgress && progress >= 1) {
|
||||
iterationProgress = 1;
|
||||
}
|
||||
iterationProgress === 1 && currentIteration--;
|
||||
currentIteration = Math.min(currentIteration, repeat + 1);
|
||||
/**
|
||||
* Reverse progress if we're not running in "normal" direction
|
||||
*/
|
||||
const isOddIteration = Boolean(currentIteration % 2);
|
||||
if (isOddIteration) {
|
||||
if (repeatType === "reverse") {
|
||||
iterationProgress = 1 - iterationProgress;
|
||||
if (repeatDelay) {
|
||||
iterationProgress -= repeatDelay / resolvedDuration;
|
||||
}
|
||||
}
|
||||
else if (repeatType === "mirror") {
|
||||
frameGenerator = mirroredGenerator;
|
||||
}
|
||||
}
|
||||
elapsed = clamp(0, 1, iterationProgress) * resolvedDuration;
|
||||
}
|
||||
/**
|
||||
* If we're in negative time, set state as the initial keyframe.
|
||||
* This prevents delay: x, duration: 0 animations from finishing
|
||||
* instantly.
|
||||
*/
|
||||
const state = isInDelayPhase
|
||||
? { done: false, value: keyframes[0] }
|
||||
: frameGenerator.next(elapsed);
|
||||
if (mixKeyframes) {
|
||||
state.value = mixKeyframes(state.value);
|
||||
}
|
||||
let { done } = state;
|
||||
if (!isInDelayPhase && calculatedDuration !== null) {
|
||||
done =
|
||||
this.playbackSpeed >= 0
|
||||
? this.currentTime >= totalDuration
|
||||
: this.currentTime <= 0;
|
||||
}
|
||||
const isAnimationFinished = this.holdTime === null &&
|
||||
(this.state === "finished" || (this.state === "running" && done));
|
||||
// TODO: The exception for inertia could be cleaner here
|
||||
if (isAnimationFinished && type !== inertia) {
|
||||
state.value = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
|
||||
}
|
||||
if (onUpdate) {
|
||||
onUpdate(state.value);
|
||||
}
|
||||
if (isAnimationFinished) {
|
||||
this.finish();
|
||||
}
|
||||
return state;
|
||||
}
|
||||
/**
|
||||
* Allows the returned animation to be awaited or promise-chained. Currently
|
||||
* resolves when the animation finishes at all but in a future update could/should
|
||||
* reject if its cancels.
|
||||
*/
|
||||
then(resolve, reject) {
|
||||
return this.finished.then(resolve, reject);
|
||||
}
|
||||
get duration() {
|
||||
return millisecondsToSeconds(this.calculatedDuration);
|
||||
}
|
||||
get iterationDuration() {
|
||||
const { delay = 0 } = this.options || {};
|
||||
return this.duration + millisecondsToSeconds(delay);
|
||||
}
|
||||
get time() {
|
||||
return millisecondsToSeconds(this.currentTime);
|
||||
}
|
||||
set time(newTime) {
|
||||
newTime = secondsToMilliseconds(newTime);
|
||||
this.currentTime = newTime;
|
||||
if (this.startTime === null ||
|
||||
this.holdTime !== null ||
|
||||
this.playbackSpeed === 0) {
|
||||
this.holdTime = newTime;
|
||||
}
|
||||
else if (this.driver) {
|
||||
this.startTime = this.driver.now() - newTime / this.playbackSpeed;
|
||||
}
|
||||
this.driver?.start(false);
|
||||
}
|
||||
get speed() {
|
||||
return this.playbackSpeed;
|
||||
}
|
||||
set speed(newSpeed) {
|
||||
this.updateTime(time.now());
|
||||
const hasChanged = this.playbackSpeed !== newSpeed;
|
||||
this.playbackSpeed = newSpeed;
|
||||
if (hasChanged) {
|
||||
this.time = millisecondsToSeconds(this.currentTime);
|
||||
}
|
||||
}
|
||||
play() {
|
||||
if (this.isStopped)
|
||||
return;
|
||||
const { driver = frameloopDriver, startTime } = this.options;
|
||||
if (!this.driver) {
|
||||
this.driver = driver((timestamp) => this.tick(timestamp));
|
||||
}
|
||||
this.options.onPlay?.();
|
||||
const now = this.driver.now();
|
||||
if (this.state === "finished") {
|
||||
this.updateFinished();
|
||||
this.startTime = now;
|
||||
}
|
||||
else if (this.holdTime !== null) {
|
||||
this.startTime = now - this.holdTime;
|
||||
}
|
||||
else if (!this.startTime) {
|
||||
this.startTime = startTime ?? now;
|
||||
}
|
||||
if (this.state === "finished" && this.speed < 0) {
|
||||
this.startTime += this.calculatedDuration;
|
||||
}
|
||||
this.holdTime = null;
|
||||
/**
|
||||
* Set playState to running only after we've used it in
|
||||
* the previous logic.
|
||||
*/
|
||||
this.state = "running";
|
||||
this.driver.start();
|
||||
}
|
||||
pause() {
|
||||
this.state = "paused";
|
||||
this.updateTime(time.now());
|
||||
this.holdTime = this.currentTime;
|
||||
}
|
||||
complete() {
|
||||
if (this.state !== "running") {
|
||||
this.play();
|
||||
}
|
||||
this.state = "finished";
|
||||
this.holdTime = null;
|
||||
}
|
||||
finish() {
|
||||
this.notifyFinished();
|
||||
this.teardown();
|
||||
this.state = "finished";
|
||||
this.options.onComplete?.();
|
||||
}
|
||||
cancel() {
|
||||
this.holdTime = null;
|
||||
this.startTime = 0;
|
||||
this.tick(0);
|
||||
this.teardown();
|
||||
this.options.onCancel?.();
|
||||
}
|
||||
teardown() {
|
||||
this.state = "idle";
|
||||
this.stopDriver();
|
||||
this.startTime = this.holdTime = null;
|
||||
activeAnimations.mainThread--;
|
||||
}
|
||||
stopDriver() {
|
||||
if (!this.driver)
|
||||
return;
|
||||
this.driver.stop();
|
||||
this.driver = undefined;
|
||||
}
|
||||
sample(sampleTime) {
|
||||
this.startTime = 0;
|
||||
return this.tick(sampleTime, true);
|
||||
}
|
||||
attachTimeline(timeline) {
|
||||
if (this.options.allowFlatten) {
|
||||
this.options.type = "keyframes";
|
||||
this.options.ease = "linear";
|
||||
this.initAnimation();
|
||||
}
|
||||
this.driver?.stop();
|
||||
return timeline.observe(this);
|
||||
}
|
||||
}
|
||||
// Legacy function support
|
||||
function animateValue(options) {
|
||||
return new JSAnimation(options);
|
||||
}
|
||||
|
||||
export { JSAnimation, animateValue };
|
||||
160
node_modules/motion-dom/dist/es/animation/NativeAnimation.mjs
generated
vendored
Normal file
160
node_modules/motion-dom/dist/es/animation/NativeAnimation.mjs
generated
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
import { invariant, millisecondsToSeconds, secondsToMilliseconds, noop } from 'motion-utils';
|
||||
import { setStyle } from '../render/dom/style-set.mjs';
|
||||
import { supportsScrollTimeline } from '../utils/supports/scroll-timeline.mjs';
|
||||
import { getFinalKeyframe } from './keyframes/get-final.mjs';
|
||||
import { WithPromise } from './utils/WithPromise.mjs';
|
||||
import { startWaapiAnimation } from './waapi/start-waapi-animation.mjs';
|
||||
import { applyGeneratorOptions } from './waapi/utils/apply-generator.mjs';
|
||||
|
||||
/**
|
||||
* NativeAnimation implements AnimationPlaybackControls for the browser's Web Animations API.
|
||||
*/
|
||||
class NativeAnimation extends WithPromise {
|
||||
constructor(options) {
|
||||
super();
|
||||
this.finishedTime = null;
|
||||
this.isStopped = false;
|
||||
if (!options)
|
||||
return;
|
||||
const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, onComplete, } = options;
|
||||
this.isPseudoElement = Boolean(pseudoElement);
|
||||
this.allowFlatten = allowFlatten;
|
||||
this.options = options;
|
||||
invariant(typeof options.type !== "string", `Mini animate() doesn't support "type" as a string.`, "mini-spring");
|
||||
const transition = applyGeneratorOptions(options);
|
||||
this.animation = startWaapiAnimation(element, name, keyframes, transition, pseudoElement);
|
||||
if (transition.autoplay === false) {
|
||||
this.animation.pause();
|
||||
}
|
||||
this.animation.onfinish = () => {
|
||||
this.finishedTime = this.time;
|
||||
if (!pseudoElement) {
|
||||
const keyframe = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
|
||||
if (this.updateMotionValue) {
|
||||
this.updateMotionValue(keyframe);
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* If we can, we want to commit the final style as set by the user,
|
||||
* rather than the computed keyframe value supplied by the animation.
|
||||
*/
|
||||
setStyle(element, name, keyframe);
|
||||
}
|
||||
this.animation.cancel();
|
||||
}
|
||||
onComplete?.();
|
||||
this.notifyFinished();
|
||||
};
|
||||
}
|
||||
play() {
|
||||
if (this.isStopped)
|
||||
return;
|
||||
this.animation.play();
|
||||
if (this.state === "finished") {
|
||||
this.updateFinished();
|
||||
}
|
||||
}
|
||||
pause() {
|
||||
this.animation.pause();
|
||||
}
|
||||
complete() {
|
||||
this.animation.finish?.();
|
||||
}
|
||||
cancel() {
|
||||
try {
|
||||
this.animation.cancel();
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
stop() {
|
||||
if (this.isStopped)
|
||||
return;
|
||||
this.isStopped = true;
|
||||
const { state } = this;
|
||||
if (state === "idle" || state === "finished") {
|
||||
return;
|
||||
}
|
||||
if (this.updateMotionValue) {
|
||||
this.updateMotionValue();
|
||||
}
|
||||
else {
|
||||
this.commitStyles();
|
||||
}
|
||||
if (!this.isPseudoElement)
|
||||
this.cancel();
|
||||
}
|
||||
/**
|
||||
* WAAPI doesn't natively have any interruption capabilities.
|
||||
*
|
||||
* In this method, we commit styles back to the DOM before cancelling
|
||||
* the animation.
|
||||
*
|
||||
* This is designed to be overridden by NativeAnimationExtended, which
|
||||
* will create a renderless JS animation and sample it twice to calculate
|
||||
* its current value, "previous" value, and therefore allow
|
||||
* Motion to also correctly calculate velocity for any subsequent animation
|
||||
* while deferring the commit until the next animation frame.
|
||||
*/
|
||||
commitStyles() {
|
||||
if (!this.isPseudoElement) {
|
||||
this.animation.commitStyles?.();
|
||||
}
|
||||
}
|
||||
get duration() {
|
||||
const duration = this.animation.effect?.getComputedTiming?.().duration || 0;
|
||||
return millisecondsToSeconds(Number(duration));
|
||||
}
|
||||
get iterationDuration() {
|
||||
const { delay = 0 } = this.options || {};
|
||||
return this.duration + millisecondsToSeconds(delay);
|
||||
}
|
||||
get time() {
|
||||
return millisecondsToSeconds(Number(this.animation.currentTime) || 0);
|
||||
}
|
||||
set time(newTime) {
|
||||
this.finishedTime = null;
|
||||
this.animation.currentTime = secondsToMilliseconds(newTime);
|
||||
}
|
||||
/**
|
||||
* The playback speed of the animation.
|
||||
* 1 = normal speed, 2 = double speed, 0.5 = half speed.
|
||||
*/
|
||||
get speed() {
|
||||
return this.animation.playbackRate;
|
||||
}
|
||||
set speed(newSpeed) {
|
||||
// Allow backwards playback after finishing
|
||||
if (newSpeed < 0)
|
||||
this.finishedTime = null;
|
||||
this.animation.playbackRate = newSpeed;
|
||||
}
|
||||
get state() {
|
||||
return this.finishedTime !== null
|
||||
? "finished"
|
||||
: this.animation.playState;
|
||||
}
|
||||
get startTime() {
|
||||
return Number(this.animation.startTime);
|
||||
}
|
||||
set startTime(newStartTime) {
|
||||
this.animation.startTime = newStartTime;
|
||||
}
|
||||
/**
|
||||
* Attaches a timeline to the animation, for instance the `ScrollTimeline`.
|
||||
*/
|
||||
attachTimeline({ timeline, observe }) {
|
||||
if (this.allowFlatten) {
|
||||
this.animation.effect?.updateTiming({ easing: "linear" });
|
||||
}
|
||||
this.animation.onfinish = null;
|
||||
if (timeline && supportsScrollTimeline()) {
|
||||
this.animation.timeline = timeline;
|
||||
return noop;
|
||||
}
|
||||
else {
|
||||
return observe(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { NativeAnimation };
|
||||
65
node_modules/motion-dom/dist/es/animation/NativeAnimationExtended.mjs
generated
vendored
Normal file
65
node_modules/motion-dom/dist/es/animation/NativeAnimationExtended.mjs
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
import { secondsToMilliseconds } from 'motion-utils';
|
||||
import { JSAnimation } from './JSAnimation.mjs';
|
||||
import { NativeAnimation } from './NativeAnimation.mjs';
|
||||
import { replaceTransitionType } from './utils/replace-transition-type.mjs';
|
||||
import { replaceStringEasing } from './waapi/utils/unsupported-easing.mjs';
|
||||
|
||||
/**
|
||||
* 10ms is chosen here as it strikes a balance between smooth
|
||||
* results (more than one keyframe per frame at 60fps) and
|
||||
* keyframe quantity.
|
||||
*/
|
||||
const sampleDelta = 10; //ms
|
||||
class NativeAnimationExtended extends NativeAnimation {
|
||||
constructor(options) {
|
||||
/**
|
||||
* The base NativeAnimation function only supports a subset
|
||||
* of Motion easings, and WAAPI also only supports some
|
||||
* easing functions via string/cubic-bezier definitions.
|
||||
*
|
||||
* This function replaces those unsupported easing functions
|
||||
* with a JS easing function. This will later get compiled
|
||||
* to a linear() easing function.
|
||||
*/
|
||||
replaceStringEasing(options);
|
||||
/**
|
||||
* Ensure we replace the transition type with a generator function
|
||||
* before passing to WAAPI.
|
||||
*
|
||||
* TODO: Does this have a better home? It could be shared with
|
||||
* JSAnimation.
|
||||
*/
|
||||
replaceTransitionType(options);
|
||||
super(options);
|
||||
if (options.startTime) {
|
||||
this.startTime = options.startTime;
|
||||
}
|
||||
this.options = options;
|
||||
}
|
||||
/**
|
||||
* WAAPI doesn't natively have any interruption capabilities.
|
||||
*
|
||||
* Rather than read commited styles back out of the DOM, we can
|
||||
* create a renderless JS animation and sample it twice to calculate
|
||||
* its current value, "previous" value, and therefore allow
|
||||
* Motion to calculate velocity for any subsequent animation.
|
||||
*/
|
||||
updateMotionValue(value) {
|
||||
const { motionValue, onUpdate, onComplete, element, ...options } = this.options;
|
||||
if (!motionValue)
|
||||
return;
|
||||
if (value !== undefined) {
|
||||
motionValue.set(value);
|
||||
return;
|
||||
}
|
||||
const sampleAnimation = new JSAnimation({
|
||||
...options,
|
||||
autoplay: false,
|
||||
});
|
||||
const sampleTime = secondsToMilliseconds(this.finishedTime ?? this.time);
|
||||
motionValue.setWithVelocity(sampleAnimation.sample(sampleTime - sampleDelta).value, sampleAnimation.sample(sampleTime).value, sampleDelta);
|
||||
sampleAnimation.stop();
|
||||
}
|
||||
}
|
||||
|
||||
export { NativeAnimationExtended };
|
||||
14
node_modules/motion-dom/dist/es/animation/NativeAnimationWrapper.mjs
generated
vendored
Normal file
14
node_modules/motion-dom/dist/es/animation/NativeAnimationWrapper.mjs
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import { NativeAnimation } from './NativeAnimation.mjs';
|
||||
|
||||
class NativeAnimationWrapper extends NativeAnimation {
|
||||
constructor(animation) {
|
||||
super();
|
||||
this.animation = animation;
|
||||
animation.onfinish = () => {
|
||||
this.finishedTime = this.time;
|
||||
this.notifyFinished();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export { NativeAnimationWrapper };
|
||||
17
node_modules/motion-dom/dist/es/animation/drivers/frame.mjs
generated
vendored
Normal file
17
node_modules/motion-dom/dist/es/animation/drivers/frame.mjs
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import { time } from '../../frameloop/sync-time.mjs';
|
||||
import { frame, cancelFrame, frameData } from '../../frameloop/frame.mjs';
|
||||
|
||||
const frameloopDriver = (update) => {
|
||||
const passTimestamp = ({ timestamp }) => update(timestamp);
|
||||
return {
|
||||
start: (keepAlive = true) => frame.update(passTimestamp, keepAlive),
|
||||
stop: () => cancelFrame(passTimestamp),
|
||||
/**
|
||||
* If we're processing this frame we can use the
|
||||
* framelocked timestamp to keep things in sync.
|
||||
*/
|
||||
now: () => (frameData.isProcessing ? frameData.timestamp : time.now()),
|
||||
};
|
||||
};
|
||||
|
||||
export { frameloopDriver };
|
||||
87
node_modules/motion-dom/dist/es/animation/generators/inertia.mjs
generated
vendored
Normal file
87
node_modules/motion-dom/dist/es/animation/generators/inertia.mjs
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
import { spring } from './spring/index.mjs';
|
||||
import { calcGeneratorVelocity } from './utils/velocity.mjs';
|
||||
|
||||
function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) {
|
||||
const origin = keyframes[0];
|
||||
const state = {
|
||||
done: false,
|
||||
value: origin,
|
||||
};
|
||||
const isOutOfBounds = (v) => (min !== undefined && v < min) || (max !== undefined && v > max);
|
||||
const nearestBoundary = (v) => {
|
||||
if (min === undefined)
|
||||
return max;
|
||||
if (max === undefined)
|
||||
return min;
|
||||
return Math.abs(min - v) < Math.abs(max - v) ? min : max;
|
||||
};
|
||||
let amplitude = power * velocity;
|
||||
const ideal = origin + amplitude;
|
||||
const target = modifyTarget === undefined ? ideal : modifyTarget(ideal);
|
||||
/**
|
||||
* If the target has changed we need to re-calculate the amplitude, otherwise
|
||||
* the animation will start from the wrong position.
|
||||
*/
|
||||
if (target !== ideal)
|
||||
amplitude = target - origin;
|
||||
const calcDelta = (t) => -amplitude * Math.exp(-t / timeConstant);
|
||||
const calcLatest = (t) => target + calcDelta(t);
|
||||
const applyFriction = (t) => {
|
||||
const delta = calcDelta(t);
|
||||
const latest = calcLatest(t);
|
||||
state.done = Math.abs(delta) <= restDelta;
|
||||
state.value = state.done ? target : latest;
|
||||
};
|
||||
/**
|
||||
* Ideally this would resolve for t in a stateless way, we could
|
||||
* do that by always precalculating the animation but as we know
|
||||
* this will be done anyway we can assume that spring will
|
||||
* be discovered during that.
|
||||
*/
|
||||
let timeReachedBoundary;
|
||||
let spring$1;
|
||||
const checkCatchBoundary = (t) => {
|
||||
if (!isOutOfBounds(state.value))
|
||||
return;
|
||||
timeReachedBoundary = t;
|
||||
spring$1 = spring({
|
||||
keyframes: [state.value, nearestBoundary(state.value)],
|
||||
velocity: calcGeneratorVelocity(calcLatest, t, state.value), // TODO: This should be passing * 1000
|
||||
damping: bounceDamping,
|
||||
stiffness: bounceStiffness,
|
||||
restDelta,
|
||||
restSpeed,
|
||||
});
|
||||
};
|
||||
checkCatchBoundary(0);
|
||||
return {
|
||||
calculatedDuration: null,
|
||||
next: (t) => {
|
||||
/**
|
||||
* We need to resolve the friction to figure out if we need a
|
||||
* spring but we don't want to do this twice per frame. So here
|
||||
* we flag if we updated for this frame and later if we did
|
||||
* we can skip doing it again.
|
||||
*/
|
||||
let hasUpdatedFrame = false;
|
||||
if (!spring$1 && timeReachedBoundary === undefined) {
|
||||
hasUpdatedFrame = true;
|
||||
applyFriction(t);
|
||||
checkCatchBoundary(t);
|
||||
}
|
||||
/**
|
||||
* If we have a spring and the provided t is beyond the moment the friction
|
||||
* animation crossed the min/max boundary, use the spring.
|
||||
*/
|
||||
if (timeReachedBoundary !== undefined && t >= timeReachedBoundary) {
|
||||
return spring$1.next(t - timeReachedBoundary);
|
||||
}
|
||||
else {
|
||||
!hasUpdatedFrame && applyFriction(t);
|
||||
return state;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export { inertia };
|
||||
49
node_modules/motion-dom/dist/es/animation/generators/keyframes.mjs
generated
vendored
Normal file
49
node_modules/motion-dom/dist/es/animation/generators/keyframes.mjs
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
import { easeInOut, isEasingArray, easingDefinitionToFunction } from 'motion-utils';
|
||||
import { interpolate } from '../../utils/interpolate.mjs';
|
||||
import { defaultOffset } from '../keyframes/offsets/default.mjs';
|
||||
import { convertOffsetToTimes } from '../keyframes/offsets/time.mjs';
|
||||
|
||||
function defaultEasing(values, easing) {
|
||||
return values.map(() => easing || easeInOut).splice(0, values.length - 1);
|
||||
}
|
||||
function keyframes({ duration = 300, keyframes: keyframeValues, times, ease = "easeInOut", }) {
|
||||
/**
|
||||
* Easing functions can be externally defined as strings. Here we convert them
|
||||
* into actual functions.
|
||||
*/
|
||||
const easingFunctions = isEasingArray(ease)
|
||||
? ease.map(easingDefinitionToFunction)
|
||||
: easingDefinitionToFunction(ease);
|
||||
/**
|
||||
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
||||
* to reduce GC during animation.
|
||||
*/
|
||||
const state = {
|
||||
done: false,
|
||||
value: keyframeValues[0],
|
||||
};
|
||||
/**
|
||||
* Create a times array based on the provided 0-1 offsets
|
||||
*/
|
||||
const absoluteTimes = convertOffsetToTimes(
|
||||
// Only use the provided offsets if they're the correct length
|
||||
// TODO Maybe we should warn here if there's a length mismatch
|
||||
times && times.length === keyframeValues.length
|
||||
? times
|
||||
: defaultOffset(keyframeValues), duration);
|
||||
const mapTimeToKeyframe = interpolate(absoluteTimes, keyframeValues, {
|
||||
ease: Array.isArray(easingFunctions)
|
||||
? easingFunctions
|
||||
: defaultEasing(keyframeValues, easingFunctions),
|
||||
});
|
||||
return {
|
||||
calculatedDuration: duration,
|
||||
next: (t) => {
|
||||
state.value = mapTimeToKeyframe(t);
|
||||
state.done = t >= duration;
|
||||
return state;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export { defaultEasing, keyframes };
|
||||
27
node_modules/motion-dom/dist/es/animation/generators/spring/defaults.mjs
generated
vendored
Normal file
27
node_modules/motion-dom/dist/es/animation/generators/spring/defaults.mjs
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
const springDefaults = {
|
||||
// Default spring physics
|
||||
stiffness: 100,
|
||||
damping: 10,
|
||||
mass: 1.0,
|
||||
velocity: 0.0,
|
||||
// Default duration/bounce-based options
|
||||
duration: 800, // in ms
|
||||
bounce: 0.3,
|
||||
visualDuration: 0.3, // in seconds
|
||||
// Rest thresholds
|
||||
restSpeed: {
|
||||
granular: 0.01,
|
||||
default: 2,
|
||||
},
|
||||
restDelta: {
|
||||
granular: 0.005,
|
||||
default: 0.5,
|
||||
},
|
||||
// Limits
|
||||
minDuration: 0.01, // in seconds
|
||||
maxDuration: 10.0, // in seconds
|
||||
minDamping: 0.05,
|
||||
maxDamping: 1,
|
||||
};
|
||||
|
||||
export { springDefaults };
|
||||
84
node_modules/motion-dom/dist/es/animation/generators/spring/find.mjs
generated
vendored
Normal file
84
node_modules/motion-dom/dist/es/animation/generators/spring/find.mjs
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
import { warning, secondsToMilliseconds, clamp, millisecondsToSeconds } from 'motion-utils';
|
||||
import { springDefaults } from './defaults.mjs';
|
||||
|
||||
const safeMin = 0.001;
|
||||
function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass, }) {
|
||||
let envelope;
|
||||
let derivative;
|
||||
warning(duration <= secondsToMilliseconds(springDefaults.maxDuration), "Spring duration must be 10 seconds or less", "spring-duration-limit");
|
||||
let dampingRatio = 1 - bounce;
|
||||
/**
|
||||
* Restrict dampingRatio and duration to within acceptable ranges.
|
||||
*/
|
||||
dampingRatio = clamp(springDefaults.minDamping, springDefaults.maxDamping, dampingRatio);
|
||||
duration = clamp(springDefaults.minDuration, springDefaults.maxDuration, millisecondsToSeconds(duration));
|
||||
if (dampingRatio < 1) {
|
||||
/**
|
||||
* Underdamped spring
|
||||
*/
|
||||
envelope = (undampedFreq) => {
|
||||
const exponentialDecay = undampedFreq * dampingRatio;
|
||||
const delta = exponentialDecay * duration;
|
||||
const a = exponentialDecay - velocity;
|
||||
const b = calcAngularFreq(undampedFreq, dampingRatio);
|
||||
const c = Math.exp(-delta);
|
||||
return safeMin - (a / b) * c;
|
||||
};
|
||||
derivative = (undampedFreq) => {
|
||||
const exponentialDecay = undampedFreq * dampingRatio;
|
||||
const delta = exponentialDecay * duration;
|
||||
const d = delta * velocity + velocity;
|
||||
const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration;
|
||||
const f = Math.exp(-delta);
|
||||
const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio);
|
||||
const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1;
|
||||
return (factor * ((d - e) * f)) / g;
|
||||
};
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* Critically-damped spring
|
||||
*/
|
||||
envelope = (undampedFreq) => {
|
||||
const a = Math.exp(-undampedFreq * duration);
|
||||
const b = (undampedFreq - velocity) * duration + 1;
|
||||
return -safeMin + a * b;
|
||||
};
|
||||
derivative = (undampedFreq) => {
|
||||
const a = Math.exp(-undampedFreq * duration);
|
||||
const b = (velocity - undampedFreq) * (duration * duration);
|
||||
return a * b;
|
||||
};
|
||||
}
|
||||
const initialGuess = 5 / duration;
|
||||
const undampedFreq = approximateRoot(envelope, derivative, initialGuess);
|
||||
duration = secondsToMilliseconds(duration);
|
||||
if (isNaN(undampedFreq)) {
|
||||
return {
|
||||
stiffness: springDefaults.stiffness,
|
||||
damping: springDefaults.damping,
|
||||
duration,
|
||||
};
|
||||
}
|
||||
else {
|
||||
const stiffness = Math.pow(undampedFreq, 2) * mass;
|
||||
return {
|
||||
stiffness,
|
||||
damping: dampingRatio * 2 * Math.sqrt(mass * stiffness),
|
||||
duration,
|
||||
};
|
||||
}
|
||||
}
|
||||
const rootIterations = 12;
|
||||
function approximateRoot(envelope, derivative, initialGuess) {
|
||||
let result = initialGuess;
|
||||
for (let i = 1; i < rootIterations; i++) {
|
||||
result = result - envelope(result) / derivative(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function calcAngularFreq(undampedFreq, dampingRatio) {
|
||||
return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
|
||||
}
|
||||
|
||||
export { calcAngularFreq, findSpring };
|
||||
175
node_modules/motion-dom/dist/es/animation/generators/spring/index.mjs
generated
vendored
Normal file
175
node_modules/motion-dom/dist/es/animation/generators/spring/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
import { millisecondsToSeconds, secondsToMilliseconds, clamp } from 'motion-utils';
|
||||
import { generateLinearEasing } from '../../waapi/utils/linear.mjs';
|
||||
import { calcGeneratorDuration, maxGeneratorDuration } from '../utils/calc-duration.mjs';
|
||||
import { createGeneratorEasing } from '../utils/create-generator-easing.mjs';
|
||||
import { calcGeneratorVelocity } from '../utils/velocity.mjs';
|
||||
import { springDefaults } from './defaults.mjs';
|
||||
import { findSpring, calcAngularFreq } from './find.mjs';
|
||||
|
||||
const durationKeys = ["duration", "bounce"];
|
||||
const physicsKeys = ["stiffness", "damping", "mass"];
|
||||
function isSpringType(options, keys) {
|
||||
return keys.some((key) => options[key] !== undefined);
|
||||
}
|
||||
function getSpringOptions(options) {
|
||||
let springOptions = {
|
||||
velocity: springDefaults.velocity,
|
||||
stiffness: springDefaults.stiffness,
|
||||
damping: springDefaults.damping,
|
||||
mass: springDefaults.mass,
|
||||
isResolvedFromDuration: false,
|
||||
...options,
|
||||
};
|
||||
// stiffness/damping/mass overrides duration/bounce
|
||||
if (!isSpringType(options, physicsKeys) &&
|
||||
isSpringType(options, durationKeys)) {
|
||||
if (options.visualDuration) {
|
||||
const visualDuration = options.visualDuration;
|
||||
const root = (2 * Math.PI) / (visualDuration * 1.2);
|
||||
const stiffness = root * root;
|
||||
const damping = 2 *
|
||||
clamp(0.05, 1, 1 - (options.bounce || 0)) *
|
||||
Math.sqrt(stiffness);
|
||||
springOptions = {
|
||||
...springOptions,
|
||||
mass: springDefaults.mass,
|
||||
stiffness,
|
||||
damping,
|
||||
};
|
||||
}
|
||||
else {
|
||||
const derived = findSpring(options);
|
||||
springOptions = {
|
||||
...springOptions,
|
||||
...derived,
|
||||
mass: springDefaults.mass,
|
||||
};
|
||||
springOptions.isResolvedFromDuration = true;
|
||||
}
|
||||
}
|
||||
return springOptions;
|
||||
}
|
||||
function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce = springDefaults.bounce) {
|
||||
const options = typeof optionsOrVisualDuration !== "object"
|
||||
? {
|
||||
visualDuration: optionsOrVisualDuration,
|
||||
keyframes: [0, 1],
|
||||
bounce,
|
||||
}
|
||||
: optionsOrVisualDuration;
|
||||
let { restSpeed, restDelta } = options;
|
||||
const origin = options.keyframes[0];
|
||||
const target = options.keyframes[options.keyframes.length - 1];
|
||||
/**
|
||||
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
||||
* to reduce GC during animation.
|
||||
*/
|
||||
const state = { done: false, value: origin };
|
||||
const { stiffness, damping, mass, duration, velocity, isResolvedFromDuration, } = getSpringOptions({
|
||||
...options,
|
||||
velocity: -millisecondsToSeconds(options.velocity || 0),
|
||||
});
|
||||
const initialVelocity = velocity || 0.0;
|
||||
const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
|
||||
const initialDelta = target - origin;
|
||||
const undampedAngularFreq = millisecondsToSeconds(Math.sqrt(stiffness / mass));
|
||||
/**
|
||||
* If we're working on a granular scale, use smaller defaults for determining
|
||||
* when the spring is finished.
|
||||
*
|
||||
* These defaults have been selected emprically based on what strikes a good
|
||||
* ratio between feeling good and finishing as soon as changes are imperceptible.
|
||||
*/
|
||||
const isGranularScale = Math.abs(initialDelta) < 5;
|
||||
restSpeed || (restSpeed = isGranularScale
|
||||
? springDefaults.restSpeed.granular
|
||||
: springDefaults.restSpeed.default);
|
||||
restDelta || (restDelta = isGranularScale
|
||||
? springDefaults.restDelta.granular
|
||||
: springDefaults.restDelta.default);
|
||||
let resolveSpring;
|
||||
if (dampingRatio < 1) {
|
||||
const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
|
||||
// Underdamped spring
|
||||
resolveSpring = (t) => {
|
||||
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
||||
return (target -
|
||||
envelope *
|
||||
(((initialVelocity +
|
||||
dampingRatio * undampedAngularFreq * initialDelta) /
|
||||
angularFreq) *
|
||||
Math.sin(angularFreq * t) +
|
||||
initialDelta * Math.cos(angularFreq * t)));
|
||||
};
|
||||
}
|
||||
else if (dampingRatio === 1) {
|
||||
// Critically damped spring
|
||||
resolveSpring = (t) => target -
|
||||
Math.exp(-undampedAngularFreq * t) *
|
||||
(initialDelta +
|
||||
(initialVelocity + undampedAngularFreq * initialDelta) * t);
|
||||
}
|
||||
else {
|
||||
// Overdamped spring
|
||||
const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1);
|
||||
resolveSpring = (t) => {
|
||||
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
||||
// When performing sinh or cosh values can hit Infinity so we cap them here
|
||||
const freqForT = Math.min(dampedAngularFreq * t, 300);
|
||||
return (target -
|
||||
(envelope *
|
||||
((initialVelocity +
|
||||
dampingRatio * undampedAngularFreq * initialDelta) *
|
||||
Math.sinh(freqForT) +
|
||||
dampedAngularFreq *
|
||||
initialDelta *
|
||||
Math.cosh(freqForT))) /
|
||||
dampedAngularFreq);
|
||||
};
|
||||
}
|
||||
const generator = {
|
||||
calculatedDuration: isResolvedFromDuration ? duration || null : null,
|
||||
next: (t) => {
|
||||
const current = resolveSpring(t);
|
||||
if (!isResolvedFromDuration) {
|
||||
let currentVelocity = t === 0 ? initialVelocity : 0.0;
|
||||
/**
|
||||
* We only need to calculate velocity for under-damped springs
|
||||
* as over- and critically-damped springs can't overshoot, so
|
||||
* checking only for displacement is enough.
|
||||
*/
|
||||
if (dampingRatio < 1) {
|
||||
currentVelocity =
|
||||
t === 0
|
||||
? secondsToMilliseconds(initialVelocity)
|
||||
: calcGeneratorVelocity(resolveSpring, t, current);
|
||||
}
|
||||
const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
|
||||
const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta;
|
||||
state.done =
|
||||
isBelowVelocityThreshold && isBelowDisplacementThreshold;
|
||||
}
|
||||
else {
|
||||
state.done = t >= duration;
|
||||
}
|
||||
state.value = state.done ? target : current;
|
||||
return state;
|
||||
},
|
||||
toString: () => {
|
||||
const calculatedDuration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
|
||||
const easing = generateLinearEasing((progress) => generator.next(calculatedDuration * progress).value, calculatedDuration, 30);
|
||||
return calculatedDuration + "ms " + easing;
|
||||
},
|
||||
toTransition: () => { },
|
||||
};
|
||||
return generator;
|
||||
}
|
||||
spring.applyToOptions = (options) => {
|
||||
const generatorOptions = createGeneratorEasing(options, 100, spring);
|
||||
options.ease = generatorOptions.ease;
|
||||
options.duration = secondsToMilliseconds(generatorOptions.duration);
|
||||
options.type = "keyframes";
|
||||
return options;
|
||||
};
|
||||
|
||||
export { spring };
|
||||
17
node_modules/motion-dom/dist/es/animation/generators/utils/calc-duration.mjs
generated
vendored
Normal file
17
node_modules/motion-dom/dist/es/animation/generators/utils/calc-duration.mjs
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Implement a practical max duration for keyframe generation
|
||||
* to prevent infinite loops
|
||||
*/
|
||||
const maxGeneratorDuration = 20000;
|
||||
function calcGeneratorDuration(generator) {
|
||||
let duration = 0;
|
||||
const timeStep = 50;
|
||||
let state = generator.next(duration);
|
||||
while (!state.done && duration < maxGeneratorDuration) {
|
||||
duration += timeStep;
|
||||
state = generator.next(duration);
|
||||
}
|
||||
return duration >= maxGeneratorDuration ? Infinity : duration;
|
||||
}
|
||||
|
||||
export { calcGeneratorDuration, maxGeneratorDuration };
|
||||
19
node_modules/motion-dom/dist/es/animation/generators/utils/create-generator-easing.mjs
generated
vendored
Normal file
19
node_modules/motion-dom/dist/es/animation/generators/utils/create-generator-easing.mjs
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
import { millisecondsToSeconds } from 'motion-utils';
|
||||
import { calcGeneratorDuration, maxGeneratorDuration } from './calc-duration.mjs';
|
||||
|
||||
/**
|
||||
* Create a progress => progress easing function from a generator.
|
||||
*/
|
||||
function createGeneratorEasing(options, scale = 100, createGenerator) {
|
||||
const generator = createGenerator({ ...options, keyframes: [0, scale] });
|
||||
const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
|
||||
return {
|
||||
type: "keyframes",
|
||||
ease: (progress) => {
|
||||
return generator.next(duration * progress).value / scale;
|
||||
},
|
||||
duration: millisecondsToSeconds(duration),
|
||||
};
|
||||
}
|
||||
|
||||
export { createGeneratorEasing };
|
||||
5
node_modules/motion-dom/dist/es/animation/generators/utils/is-generator.mjs
generated
vendored
Normal file
5
node_modules/motion-dom/dist/es/animation/generators/utils/is-generator.mjs
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
function isGenerator(type) {
|
||||
return typeof type === "function" && "applyToOptions" in type;
|
||||
}
|
||||
|
||||
export { isGenerator };
|
||||
9
node_modules/motion-dom/dist/es/animation/generators/utils/velocity.mjs
generated
vendored
Normal file
9
node_modules/motion-dom/dist/es/animation/generators/utils/velocity.mjs
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { velocityPerSecond } from 'motion-utils';
|
||||
|
||||
const velocitySampleDuration = 5; // ms
|
||||
function calcGeneratorVelocity(resolveValue, t, current) {
|
||||
const prevT = Math.max(t - velocitySampleDuration, 0);
|
||||
return velocityPerSecond(current - resolveValue(prevT), t - prevT);
|
||||
}
|
||||
|
||||
export { calcGeneratorVelocity };
|
||||
131
node_modules/motion-dom/dist/es/animation/keyframes/DOMKeyframesResolver.mjs
generated
vendored
Normal file
131
node_modules/motion-dom/dist/es/animation/keyframes/DOMKeyframesResolver.mjs
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
import { positionalKeys } from '../../render/utils/keys-position.mjs';
|
||||
import { findDimensionValueType } from '../../value/types/dimensions.mjs';
|
||||
import { getVariableValue } from '../utils/css-variables-conversion.mjs';
|
||||
import { isCSSVariableToken } from '../utils/is-css-variable.mjs';
|
||||
import { KeyframeResolver } from './KeyframesResolver.mjs';
|
||||
import { isNone } from './utils/is-none.mjs';
|
||||
import { makeNoneKeyframesAnimatable } from './utils/make-none-animatable.mjs';
|
||||
import { isNumOrPxType, positionalValues } from './utils/unit-conversion.mjs';
|
||||
|
||||
class DOMKeyframesResolver extends KeyframeResolver {
|
||||
constructor(unresolvedKeyframes, onComplete, name, motionValue, element) {
|
||||
super(unresolvedKeyframes, onComplete, name, motionValue, element, true);
|
||||
}
|
||||
readKeyframes() {
|
||||
const { unresolvedKeyframes, element, name } = this;
|
||||
if (!element || !element.current)
|
||||
return;
|
||||
super.readKeyframes();
|
||||
/**
|
||||
* If any keyframe is a CSS variable, we need to find its value by sampling the element
|
||||
*/
|
||||
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
||||
let keyframe = unresolvedKeyframes[i];
|
||||
if (typeof keyframe === "string") {
|
||||
keyframe = keyframe.trim();
|
||||
if (isCSSVariableToken(keyframe)) {
|
||||
const resolved = getVariableValue(keyframe, element.current);
|
||||
if (resolved !== undefined) {
|
||||
unresolvedKeyframes[i] = resolved;
|
||||
}
|
||||
if (i === unresolvedKeyframes.length - 1) {
|
||||
this.finalKeyframe = keyframe;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Resolve "none" values. We do this potentially twice - once before and once after measuring keyframes.
|
||||
* This could be seen as inefficient but it's a trade-off to avoid measurements in more situations, which
|
||||
* have a far bigger performance impact.
|
||||
*/
|
||||
this.resolveNoneKeyframes();
|
||||
/**
|
||||
* Check to see if unit type has changed. If so schedule jobs that will
|
||||
* temporarily set styles to the destination keyframes.
|
||||
* Skip if we have more than two keyframes or this isn't a positional value.
|
||||
* TODO: We can throw if there are multiple keyframes and the value type changes.
|
||||
*/
|
||||
if (!positionalKeys.has(name) || unresolvedKeyframes.length !== 2) {
|
||||
return;
|
||||
}
|
||||
const [origin, target] = unresolvedKeyframes;
|
||||
const originType = findDimensionValueType(origin);
|
||||
const targetType = findDimensionValueType(target);
|
||||
/**
|
||||
* Either we don't recognise these value types or we can animate between them.
|
||||
*/
|
||||
if (originType === targetType)
|
||||
return;
|
||||
/**
|
||||
* If both values are numbers or pixels, we can animate between them by
|
||||
* converting them to numbers.
|
||||
*/
|
||||
if (isNumOrPxType(originType) && isNumOrPxType(targetType)) {
|
||||
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
||||
const value = unresolvedKeyframes[i];
|
||||
if (typeof value === "string") {
|
||||
unresolvedKeyframes[i] = parseFloat(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (positionalValues[name]) {
|
||||
/**
|
||||
* Else, the only way to resolve this is by measuring the element.
|
||||
*/
|
||||
this.needsMeasurement = true;
|
||||
}
|
||||
}
|
||||
resolveNoneKeyframes() {
|
||||
const { unresolvedKeyframes, name } = this;
|
||||
const noneKeyframeIndexes = [];
|
||||
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
||||
if (unresolvedKeyframes[i] === null ||
|
||||
isNone(unresolvedKeyframes[i])) {
|
||||
noneKeyframeIndexes.push(i);
|
||||
}
|
||||
}
|
||||
if (noneKeyframeIndexes.length) {
|
||||
makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name);
|
||||
}
|
||||
}
|
||||
measureInitialState() {
|
||||
const { element, unresolvedKeyframes, name } = this;
|
||||
if (!element || !element.current)
|
||||
return;
|
||||
if (name === "height") {
|
||||
this.suspendedScrollY = window.pageYOffset;
|
||||
}
|
||||
this.measuredOrigin = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
|
||||
unresolvedKeyframes[0] = this.measuredOrigin;
|
||||
// Set final key frame to measure after next render
|
||||
const measureKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
|
||||
if (measureKeyframe !== undefined) {
|
||||
element.getValue(name, measureKeyframe).jump(measureKeyframe, false);
|
||||
}
|
||||
}
|
||||
measureEndState() {
|
||||
const { element, name, unresolvedKeyframes } = this;
|
||||
if (!element || !element.current)
|
||||
return;
|
||||
const value = element.getValue(name);
|
||||
value && value.jump(this.measuredOrigin, false);
|
||||
const finalKeyframeIndex = unresolvedKeyframes.length - 1;
|
||||
const finalKeyframe = unresolvedKeyframes[finalKeyframeIndex];
|
||||
unresolvedKeyframes[finalKeyframeIndex] = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
|
||||
if (finalKeyframe !== null && this.finalKeyframe === undefined) {
|
||||
this.finalKeyframe = finalKeyframe;
|
||||
}
|
||||
// If we removed transform values, reapply them before the next render
|
||||
if (this.removedTransforms?.length) {
|
||||
this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => {
|
||||
element
|
||||
.getValue(unsetTransformName)
|
||||
.set(unsetTransformValue);
|
||||
});
|
||||
}
|
||||
this.resolveNoneKeyframes();
|
||||
}
|
||||
}
|
||||
|
||||
export { DOMKeyframesResolver };
|
||||
147
node_modules/motion-dom/dist/es/animation/keyframes/KeyframesResolver.mjs
generated
vendored
Normal file
147
node_modules/motion-dom/dist/es/animation/keyframes/KeyframesResolver.mjs
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
import { fillWildcards } from './utils/fill-wildcards.mjs';
|
||||
import { removeNonTranslationalTransform } from './utils/unit-conversion.mjs';
|
||||
import { frame } from '../../frameloop/frame.mjs';
|
||||
|
||||
const toResolve = new Set();
|
||||
let isScheduled = false;
|
||||
let anyNeedsMeasurement = false;
|
||||
let isForced = false;
|
||||
function measureAllKeyframes() {
|
||||
if (anyNeedsMeasurement) {
|
||||
const resolversToMeasure = Array.from(toResolve).filter((resolver) => resolver.needsMeasurement);
|
||||
const elementsToMeasure = new Set(resolversToMeasure.map((resolver) => resolver.element));
|
||||
const transformsToRestore = new Map();
|
||||
/**
|
||||
* Write pass
|
||||
* If we're measuring elements we want to remove bounding box-changing transforms.
|
||||
*/
|
||||
elementsToMeasure.forEach((element) => {
|
||||
const removedTransforms = removeNonTranslationalTransform(element);
|
||||
if (!removedTransforms.length)
|
||||
return;
|
||||
transformsToRestore.set(element, removedTransforms);
|
||||
element.render();
|
||||
});
|
||||
// Read
|
||||
resolversToMeasure.forEach((resolver) => resolver.measureInitialState());
|
||||
// Write
|
||||
elementsToMeasure.forEach((element) => {
|
||||
element.render();
|
||||
const restore = transformsToRestore.get(element);
|
||||
if (restore) {
|
||||
restore.forEach(([key, value]) => {
|
||||
element.getValue(key)?.set(value);
|
||||
});
|
||||
}
|
||||
});
|
||||
// Read
|
||||
resolversToMeasure.forEach((resolver) => resolver.measureEndState());
|
||||
// Write
|
||||
resolversToMeasure.forEach((resolver) => {
|
||||
if (resolver.suspendedScrollY !== undefined) {
|
||||
window.scrollTo(0, resolver.suspendedScrollY);
|
||||
}
|
||||
});
|
||||
}
|
||||
anyNeedsMeasurement = false;
|
||||
isScheduled = false;
|
||||
toResolve.forEach((resolver) => resolver.complete(isForced));
|
||||
toResolve.clear();
|
||||
}
|
||||
function readAllKeyframes() {
|
||||
toResolve.forEach((resolver) => {
|
||||
resolver.readKeyframes();
|
||||
if (resolver.needsMeasurement) {
|
||||
anyNeedsMeasurement = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
function flushKeyframeResolvers() {
|
||||
isForced = true;
|
||||
readAllKeyframes();
|
||||
measureAllKeyframes();
|
||||
isForced = false;
|
||||
}
|
||||
class KeyframeResolver {
|
||||
constructor(unresolvedKeyframes, onComplete, name, motionValue, element, isAsync = false) {
|
||||
this.state = "pending";
|
||||
/**
|
||||
* Track whether this resolver is async. If it is, it'll be added to the
|
||||
* resolver queue and flushed in the next frame. Resolvers that aren't going
|
||||
* to trigger read/write thrashing don't need to be async.
|
||||
*/
|
||||
this.isAsync = false;
|
||||
/**
|
||||
* Track whether this resolver needs to perform a measurement
|
||||
* to resolve its keyframes.
|
||||
*/
|
||||
this.needsMeasurement = false;
|
||||
this.unresolvedKeyframes = [...unresolvedKeyframes];
|
||||
this.onComplete = onComplete;
|
||||
this.name = name;
|
||||
this.motionValue = motionValue;
|
||||
this.element = element;
|
||||
this.isAsync = isAsync;
|
||||
}
|
||||
scheduleResolve() {
|
||||
this.state = "scheduled";
|
||||
if (this.isAsync) {
|
||||
toResolve.add(this);
|
||||
if (!isScheduled) {
|
||||
isScheduled = true;
|
||||
frame.read(readAllKeyframes);
|
||||
frame.resolveKeyframes(measureAllKeyframes);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.readKeyframes();
|
||||
this.complete();
|
||||
}
|
||||
}
|
||||
readKeyframes() {
|
||||
const { unresolvedKeyframes, name, element, motionValue } = this;
|
||||
// If initial keyframe is null we need to read it from the DOM
|
||||
if (unresolvedKeyframes[0] === null) {
|
||||
const currentValue = motionValue?.get();
|
||||
// TODO: This doesn't work if the final keyframe is a wildcard
|
||||
const finalKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
|
||||
if (currentValue !== undefined) {
|
||||
unresolvedKeyframes[0] = currentValue;
|
||||
}
|
||||
else if (element && name) {
|
||||
const valueAsRead = element.readValue(name, finalKeyframe);
|
||||
if (valueAsRead !== undefined && valueAsRead !== null) {
|
||||
unresolvedKeyframes[0] = valueAsRead;
|
||||
}
|
||||
}
|
||||
if (unresolvedKeyframes[0] === undefined) {
|
||||
unresolvedKeyframes[0] = finalKeyframe;
|
||||
}
|
||||
if (motionValue && currentValue === undefined) {
|
||||
motionValue.set(unresolvedKeyframes[0]);
|
||||
}
|
||||
}
|
||||
fillWildcards(unresolvedKeyframes);
|
||||
}
|
||||
setFinalKeyframe() { }
|
||||
measureInitialState() { }
|
||||
renderEndStyles() { }
|
||||
measureEndState() { }
|
||||
complete(isForcedComplete = false) {
|
||||
this.state = "complete";
|
||||
this.onComplete(this.unresolvedKeyframes, this.finalKeyframe, isForcedComplete);
|
||||
toResolve.delete(this);
|
||||
}
|
||||
cancel() {
|
||||
if (this.state === "scheduled") {
|
||||
toResolve.delete(this);
|
||||
this.state = "pending";
|
||||
}
|
||||
}
|
||||
resume() {
|
||||
if (this.state === "pending")
|
||||
this.scheduleResolve();
|
||||
}
|
||||
}
|
||||
|
||||
export { KeyframeResolver, flushKeyframeResolvers };
|
||||
11
node_modules/motion-dom/dist/es/animation/keyframes/get-final.mjs
generated
vendored
Normal file
11
node_modules/motion-dom/dist/es/animation/keyframes/get-final.mjs
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const isNotNull = (value) => value !== null;
|
||||
function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe, speed = 1) {
|
||||
const resolvedKeyframes = keyframes.filter(isNotNull);
|
||||
const useFirstKeyframe = speed < 0 || (repeat && repeatType !== "loop" && repeat % 2 === 1);
|
||||
const index = useFirstKeyframe ? 0 : resolvedKeyframes.length - 1;
|
||||
return !index || finalKeyframe === undefined
|
||||
? resolvedKeyframes[index]
|
||||
: finalKeyframe;
|
||||
}
|
||||
|
||||
export { getFinalKeyframe };
|
||||
9
node_modules/motion-dom/dist/es/animation/keyframes/offsets/default.mjs
generated
vendored
Normal file
9
node_modules/motion-dom/dist/es/animation/keyframes/offsets/default.mjs
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { fillOffset } from './fill.mjs';
|
||||
|
||||
function defaultOffset(arr) {
|
||||
const offset = [0];
|
||||
fillOffset(offset, arr.length - 1);
|
||||
return offset;
|
||||
}
|
||||
|
||||
export { defaultOffset };
|
||||
12
node_modules/motion-dom/dist/es/animation/keyframes/offsets/fill.mjs
generated
vendored
Normal file
12
node_modules/motion-dom/dist/es/animation/keyframes/offsets/fill.mjs
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import { progress } from 'motion-utils';
|
||||
import { mixNumber } from '../../../utils/mix/number.mjs';
|
||||
|
||||
function fillOffset(offset, remaining) {
|
||||
const min = offset[offset.length - 1];
|
||||
for (let i = 1; i <= remaining; i++) {
|
||||
const offsetProgress = progress(0, remaining, i);
|
||||
offset.push(mixNumber(min, 1, offsetProgress));
|
||||
}
|
||||
}
|
||||
|
||||
export { fillOffset };
|
||||
5
node_modules/motion-dom/dist/es/animation/keyframes/offsets/time.mjs
generated
vendored
Normal file
5
node_modules/motion-dom/dist/es/animation/keyframes/offsets/time.mjs
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
function convertOffsetToTimes(offset, duration) {
|
||||
return offset.map((o) => o * duration);
|
||||
}
|
||||
|
||||
export { convertOffsetToTimes };
|
||||
11
node_modules/motion-dom/dist/es/animation/keyframes/utils/apply-px-defaults.mjs
generated
vendored
Normal file
11
node_modules/motion-dom/dist/es/animation/keyframes/utils/apply-px-defaults.mjs
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { pxValues } from '../../waapi/utils/px-values.mjs';
|
||||
|
||||
function applyPxDefaults(keyframes, name) {
|
||||
for (let i = 0; i < keyframes.length; i++) {
|
||||
if (typeof keyframes[i] === "number" && pxValues.has(name)) {
|
||||
keyframes[i] = keyframes[i] + "px";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { applyPxDefaults };
|
||||
7
node_modules/motion-dom/dist/es/animation/keyframes/utils/fill-wildcards.mjs
generated
vendored
Normal file
7
node_modules/motion-dom/dist/es/animation/keyframes/utils/fill-wildcards.mjs
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
function fillWildcards(keyframes) {
|
||||
for (let i = 1; i < keyframes.length; i++) {
|
||||
keyframes[i] ?? (keyframes[i] = keyframes[i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
export { fillWildcards };
|
||||
15
node_modules/motion-dom/dist/es/animation/keyframes/utils/is-none.mjs
generated
vendored
Normal file
15
node_modules/motion-dom/dist/es/animation/keyframes/utils/is-none.mjs
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { isZeroValueString } from 'motion-utils';
|
||||
|
||||
function isNone(value) {
|
||||
if (typeof value === "number") {
|
||||
return value === 0;
|
||||
}
|
||||
else if (value !== null) {
|
||||
return value === "none" || value === "0" || isZeroValueString(value);
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export { isNone };
|
||||
30
node_modules/motion-dom/dist/es/animation/keyframes/utils/make-none-animatable.mjs
generated
vendored
Normal file
30
node_modules/motion-dom/dist/es/animation/keyframes/utils/make-none-animatable.mjs
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import { analyseComplexValue } from '../../../value/types/complex/index.mjs';
|
||||
import { getAnimatableNone } from '../../../value/types/utils/animatable-none.mjs';
|
||||
|
||||
/**
|
||||
* If we encounter keyframes like "none" or "0" and we also have keyframes like
|
||||
* "#fff" or "200px 200px" we want to find a keyframe to serve as a template for
|
||||
* the "none" keyframes. In this case "#fff" or "200px 200px" - then these get turned into
|
||||
* zero equivalents, i.e. "#fff0" or "0px 0px".
|
||||
*/
|
||||
const invalidTemplates = new Set(["auto", "none", "0"]);
|
||||
function makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name) {
|
||||
let i = 0;
|
||||
let animatableTemplate = undefined;
|
||||
while (i < unresolvedKeyframes.length && !animatableTemplate) {
|
||||
const keyframe = unresolvedKeyframes[i];
|
||||
if (typeof keyframe === "string" &&
|
||||
!invalidTemplates.has(keyframe) &&
|
||||
analyseComplexValue(keyframe).values.length) {
|
||||
animatableTemplate = unresolvedKeyframes[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (animatableTemplate && name) {
|
||||
for (const noneIndex of noneKeyframeIndexes) {
|
||||
unresolvedKeyframes[noneIndex] = getAnimatableNone(name, animatableTemplate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { makeNoneKeyframesAnimatable };
|
||||
36
node_modules/motion-dom/dist/es/animation/keyframes/utils/unit-conversion.mjs
generated
vendored
Normal file
36
node_modules/motion-dom/dist/es/animation/keyframes/utils/unit-conversion.mjs
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
import { parseValueFromTransform } from '../../../render/dom/parse-transform.mjs';
|
||||
import { transformPropOrder } from '../../../render/utils/keys-transform.mjs';
|
||||
import { number } from '../../../value/types/numbers/index.mjs';
|
||||
import { px } from '../../../value/types/numbers/units.mjs';
|
||||
|
||||
const isNumOrPxType = (v) => v === number || v === px;
|
||||
const transformKeys = new Set(["x", "y", "z"]);
|
||||
const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
|
||||
function removeNonTranslationalTransform(visualElement) {
|
||||
const removedTransforms = [];
|
||||
nonTranslationalTransformKeys.forEach((key) => {
|
||||
const value = visualElement.getValue(key);
|
||||
if (value !== undefined) {
|
||||
removedTransforms.push([key, value.get()]);
|
||||
value.set(key.startsWith("scale") ? 1 : 0);
|
||||
}
|
||||
});
|
||||
return removedTransforms;
|
||||
}
|
||||
const positionalValues = {
|
||||
// Dimensions
|
||||
width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight),
|
||||
height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom),
|
||||
top: (_bbox, { top }) => parseFloat(top),
|
||||
left: (_bbox, { left }) => parseFloat(left),
|
||||
bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min),
|
||||
right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min),
|
||||
// Transform
|
||||
x: (_bbox, { transform }) => parseValueFromTransform(transform, "x"),
|
||||
y: (_bbox, { transform }) => parseValueFromTransform(transform, "y"),
|
||||
};
|
||||
// Alias translate longform names
|
||||
positionalValues.translateX = positionalValues.x;
|
||||
positionalValues.translateY = positionalValues.y;
|
||||
|
||||
export { isNumOrPxType, positionalValues, removeNonTranslationalTransform };
|
||||
26
node_modules/motion-dom/dist/es/animation/utils/WithPromise.mjs
generated
vendored
Normal file
26
node_modules/motion-dom/dist/es/animation/utils/WithPromise.mjs
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
class WithPromise {
|
||||
constructor() {
|
||||
this.updateFinished();
|
||||
}
|
||||
get finished() {
|
||||
return this._finished;
|
||||
}
|
||||
updateFinished() {
|
||||
this._finished = new Promise((resolve) => {
|
||||
this.resolve = resolve;
|
||||
});
|
||||
}
|
||||
notifyFinished() {
|
||||
this.resolve();
|
||||
}
|
||||
/**
|
||||
* Allows the animation to be awaited.
|
||||
*
|
||||
* @deprecated Use `finished` instead.
|
||||
*/
|
||||
then(onResolve, onReject) {
|
||||
return this.finished.then(onResolve, onReject);
|
||||
}
|
||||
}
|
||||
|
||||
export { WithPromise };
|
||||
9
node_modules/motion-dom/dist/es/animation/utils/active-animations.mjs
generated
vendored
Normal file
9
node_modules/motion-dom/dist/es/animation/utils/active-animations.mjs
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
const animationMaps = new WeakMap();
|
||||
const animationMapKey = (name, pseudoElement = "") => `${name}:${pseudoElement}`;
|
||||
function getAnimationMap(element) {
|
||||
const map = animationMaps.get(element) || new Map();
|
||||
animationMaps.set(element, map);
|
||||
return map;
|
||||
}
|
||||
|
||||
export { animationMapKey, getAnimationMap };
|
||||
42
node_modules/motion-dom/dist/es/animation/utils/can-animate.mjs
generated
vendored
Normal file
42
node_modules/motion-dom/dist/es/animation/utils/can-animate.mjs
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
import { warning } from 'motion-utils';
|
||||
import { isGenerator } from '../generators/utils/is-generator.mjs';
|
||||
import { isAnimatable } from './is-animatable.mjs';
|
||||
|
||||
function hasKeyframesChanged(keyframes) {
|
||||
const current = keyframes[0];
|
||||
if (keyframes.length === 1)
|
||||
return true;
|
||||
for (let i = 0; i < keyframes.length; i++) {
|
||||
if (keyframes[i] !== current)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
function canAnimate(keyframes, name, type, velocity) {
|
||||
/**
|
||||
* Check if we're able to animate between the start and end keyframes,
|
||||
* and throw a warning if we're attempting to animate between one that's
|
||||
* animatable and another that isn't.
|
||||
*/
|
||||
const originKeyframe = keyframes[0];
|
||||
if (originKeyframe === null)
|
||||
return false;
|
||||
/**
|
||||
* These aren't traditionally animatable but we do support them.
|
||||
* In future we could look into making this more generic or replacing
|
||||
* this function with mix() === mixImmediate
|
||||
*/
|
||||
if (name === "display" || name === "visibility")
|
||||
return true;
|
||||
const targetKeyframe = keyframes[keyframes.length - 1];
|
||||
const isOriginAnimatable = isAnimatable(originKeyframe, name);
|
||||
const isTargetAnimatable = isAnimatable(targetKeyframe, name);
|
||||
warning(isOriginAnimatable === isTargetAnimatable, `You are trying to animate ${name} from "${originKeyframe}" to "${targetKeyframe}". "${isOriginAnimatable ? targetKeyframe : originKeyframe}" is not an animatable value.`, "value-not-animatable");
|
||||
// Always skip if any of these are true
|
||||
if (!isOriginAnimatable || !isTargetAnimatable) {
|
||||
return false;
|
||||
}
|
||||
return (hasKeyframesChanged(keyframes) ||
|
||||
((type === "spring" || isGenerator(type)) && velocity));
|
||||
}
|
||||
|
||||
export { canAnimate };
|
||||
41
node_modules/motion-dom/dist/es/animation/utils/css-variables-conversion.mjs
generated
vendored
Normal file
41
node_modules/motion-dom/dist/es/animation/utils/css-variables-conversion.mjs
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
import { invariant, isNumericalString } from 'motion-utils';
|
||||
import { isCSSVariableToken } from './is-css-variable.mjs';
|
||||
|
||||
/**
|
||||
* Parse Framer's special CSS variable format into a CSS token and a fallback.
|
||||
*
|
||||
* ```
|
||||
* `var(--foo, #fff)` => [`--foo`, '#fff']
|
||||
* ```
|
||||
*
|
||||
* @param current
|
||||
*/
|
||||
const splitCSSVariableRegex =
|
||||
// eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words
|
||||
/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;
|
||||
function parseCSSVariable(current) {
|
||||
const match = splitCSSVariableRegex.exec(current);
|
||||
if (!match)
|
||||
return [,];
|
||||
const [, token1, token2, fallback] = match;
|
||||
return [`--${token1 ?? token2}`, fallback];
|
||||
}
|
||||
const maxDepth = 4;
|
||||
function getVariableValue(current, element, depth = 1) {
|
||||
invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`, "max-css-var-depth");
|
||||
const [token, fallback] = parseCSSVariable(current);
|
||||
// No CSS variable detected
|
||||
if (!token)
|
||||
return;
|
||||
// Attempt to read this CSS variable off the element
|
||||
const resolved = window.getComputedStyle(element).getPropertyValue(token);
|
||||
if (resolved) {
|
||||
const trimmed = resolved.trim();
|
||||
return isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed;
|
||||
}
|
||||
return isCSSVariableToken(fallback)
|
||||
? getVariableValue(fallback, element, depth + 1)
|
||||
: fallback;
|
||||
}
|
||||
|
||||
export { getVariableValue, parseCSSVariable };
|
||||
7
node_modules/motion-dom/dist/es/animation/utils/get-value-transition.mjs
generated
vendored
Normal file
7
node_modules/motion-dom/dist/es/animation/utils/get-value-transition.mjs
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
function getValueTransition(transition, key) {
|
||||
return (transition?.[key] ??
|
||||
transition?.["default"] ??
|
||||
transition);
|
||||
}
|
||||
|
||||
export { getValueTransition };
|
||||
30
node_modules/motion-dom/dist/es/animation/utils/is-animatable.mjs
generated
vendored
Normal file
30
node_modules/motion-dom/dist/es/animation/utils/is-animatable.mjs
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import { complex } from '../../value/types/complex/index.mjs';
|
||||
|
||||
/**
|
||||
* Check if a value is animatable. Examples:
|
||||
*
|
||||
* ✅: 100, "100px", "#fff"
|
||||
* ❌: "block", "url(2.jpg)"
|
||||
* @param value
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
const isAnimatable = (value, name) => {
|
||||
// If the list of keys that might be non-animatable grows, replace with Set
|
||||
if (name === "zIndex")
|
||||
return false;
|
||||
// If it's a number or a keyframes array, we can animate it. We might at some point
|
||||
// need to do a deep isAnimatable check of keyframes, or let Popmotion handle this,
|
||||
// but for now lets leave it like this for performance reasons
|
||||
if (typeof value === "number" || Array.isArray(value))
|
||||
return true;
|
||||
if (typeof value === "string" && // It's animatable if we have a string
|
||||
(complex.test(value) || value === "0") && // And it contains numbers and/or colors
|
||||
!value.startsWith("url(") // Unless it starts with "url("
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export { isAnimatable };
|
||||
15
node_modules/motion-dom/dist/es/animation/utils/is-css-variable.mjs
generated
vendored
Normal file
15
node_modules/motion-dom/dist/es/animation/utils/is-css-variable.mjs
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
const checkStringStartsWith = (token) => (key) => typeof key === "string" && key.startsWith(token);
|
||||
const isCSSVariableName =
|
||||
/*@__PURE__*/ checkStringStartsWith("--");
|
||||
const startsAsVariableToken =
|
||||
/*@__PURE__*/ checkStringStartsWith("var(--");
|
||||
const isCSSVariableToken = (value) => {
|
||||
const startsWithToken = startsAsVariableToken(value);
|
||||
if (!startsWithToken)
|
||||
return false;
|
||||
// Ensure any comments are stripped from the value as this can harm performance of the regex.
|
||||
return singleCssVariableRegex.test(value.split("/*")[0].trim());
|
||||
};
|
||||
const singleCssVariableRegex = /var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu;
|
||||
|
||||
export { isCSSVariableName, isCSSVariableToken };
|
||||
6
node_modules/motion-dom/dist/es/animation/utils/make-animation-instant.mjs
generated
vendored
Normal file
6
node_modules/motion-dom/dist/es/animation/utils/make-animation-instant.mjs
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
function makeAnimationInstant(options) {
|
||||
options.duration = 0;
|
||||
options.type = "keyframes";
|
||||
}
|
||||
|
||||
export { makeAnimationInstant };
|
||||
18
node_modules/motion-dom/dist/es/animation/utils/replace-transition-type.mjs
generated
vendored
Normal file
18
node_modules/motion-dom/dist/es/animation/utils/replace-transition-type.mjs
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import { inertia } from '../generators/inertia.mjs';
|
||||
import { keyframes } from '../generators/keyframes.mjs';
|
||||
import { spring } from '../generators/spring/index.mjs';
|
||||
|
||||
const transitionTypeMap = {
|
||||
decay: inertia,
|
||||
inertia,
|
||||
tween: keyframes,
|
||||
keyframes: keyframes,
|
||||
spring,
|
||||
};
|
||||
function replaceTransitionType(transition) {
|
||||
if (typeof transition.type === "string") {
|
||||
transition.type = transitionTypeMap[transition.type];
|
||||
}
|
||||
}
|
||||
|
||||
export { replaceTransitionType };
|
||||
3
node_modules/motion-dom/dist/es/animation/waapi/easing/cubic-bezier.mjs
generated
vendored
Normal file
3
node_modules/motion-dom/dist/es/animation/waapi/easing/cubic-bezier.mjs
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
|
||||
|
||||
export { cubicBezierAsString };
|
||||
14
node_modules/motion-dom/dist/es/animation/waapi/easing/is-supported.mjs
generated
vendored
Normal file
14
node_modules/motion-dom/dist/es/animation/waapi/easing/is-supported.mjs
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import { isBezierDefinition } from 'motion-utils';
|
||||
import { supportsLinearEasing } from '../../../utils/supports/linear-easing.mjs';
|
||||
import { supportedWaapiEasing } from './supported.mjs';
|
||||
|
||||
function isWaapiSupportedEasing(easing) {
|
||||
return Boolean((typeof easing === "function" && supportsLinearEasing()) ||
|
||||
!easing ||
|
||||
(typeof easing === "string" &&
|
||||
(easing in supportedWaapiEasing || supportsLinearEasing())) ||
|
||||
isBezierDefinition(easing) ||
|
||||
(Array.isArray(easing) && easing.every(isWaapiSupportedEasing)));
|
||||
}
|
||||
|
||||
export { isWaapiSupportedEasing };
|
||||
28
node_modules/motion-dom/dist/es/animation/waapi/easing/map-easing.mjs
generated
vendored
Normal file
28
node_modules/motion-dom/dist/es/animation/waapi/easing/map-easing.mjs
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
import { isBezierDefinition } from 'motion-utils';
|
||||
import { supportsLinearEasing } from '../../../utils/supports/linear-easing.mjs';
|
||||
import { generateLinearEasing } from '../utils/linear.mjs';
|
||||
import { cubicBezierAsString } from './cubic-bezier.mjs';
|
||||
import { supportedWaapiEasing } from './supported.mjs';
|
||||
|
||||
function mapEasingToNativeEasing(easing, duration) {
|
||||
if (!easing) {
|
||||
return undefined;
|
||||
}
|
||||
else if (typeof easing === "function") {
|
||||
return supportsLinearEasing()
|
||||
? generateLinearEasing(easing, duration)
|
||||
: "ease-out";
|
||||
}
|
||||
else if (isBezierDefinition(easing)) {
|
||||
return cubicBezierAsString(easing);
|
||||
}
|
||||
else if (Array.isArray(easing)) {
|
||||
return easing.map((segmentEasing) => mapEasingToNativeEasing(segmentEasing, duration) ||
|
||||
supportedWaapiEasing.easeOut);
|
||||
}
|
||||
else {
|
||||
return supportedWaapiEasing[easing];
|
||||
}
|
||||
}
|
||||
|
||||
export { mapEasingToNativeEasing };
|
||||
15
node_modules/motion-dom/dist/es/animation/waapi/easing/supported.mjs
generated
vendored
Normal file
15
node_modules/motion-dom/dist/es/animation/waapi/easing/supported.mjs
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { cubicBezierAsString } from './cubic-bezier.mjs';
|
||||
|
||||
const supportedWaapiEasing = {
|
||||
linear: "linear",
|
||||
ease: "ease",
|
||||
easeIn: "ease-in",
|
||||
easeOut: "ease-out",
|
||||
easeInOut: "ease-in-out",
|
||||
circIn: /*@__PURE__*/ cubicBezierAsString([0, 0.65, 0.55, 1]),
|
||||
circOut: /*@__PURE__*/ cubicBezierAsString([0.55, 0, 1, 0.45]),
|
||||
backIn: /*@__PURE__*/ cubicBezierAsString([0.31, 0.01, 0.66, -0.59]),
|
||||
backOut: /*@__PURE__*/ cubicBezierAsString([0.33, 1.53, 0.69, 0.99]),
|
||||
};
|
||||
|
||||
export { supportedWaapiEasing };
|
||||
39
node_modules/motion-dom/dist/es/animation/waapi/start-waapi-animation.mjs
generated
vendored
Normal file
39
node_modules/motion-dom/dist/es/animation/waapi/start-waapi-animation.mjs
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
import { activeAnimations } from '../../stats/animation-count.mjs';
|
||||
import { statsBuffer } from '../../stats/buffer.mjs';
|
||||
import { mapEasingToNativeEasing } from './easing/map-easing.mjs';
|
||||
|
||||
function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duration = 300, repeat = 0, repeatType = "loop", ease = "easeOut", times, } = {}, pseudoElement = undefined) {
|
||||
const keyframeOptions = {
|
||||
[valueName]: keyframes,
|
||||
};
|
||||
if (times)
|
||||
keyframeOptions.offset = times;
|
||||
const easing = mapEasingToNativeEasing(ease, duration);
|
||||
/**
|
||||
* If this is an easing array, apply to keyframes, not animation as a whole
|
||||
*/
|
||||
if (Array.isArray(easing))
|
||||
keyframeOptions.easing = easing;
|
||||
if (statsBuffer.value) {
|
||||
activeAnimations.waapi++;
|
||||
}
|
||||
const options = {
|
||||
delay,
|
||||
duration,
|
||||
easing: !Array.isArray(easing) ? easing : "linear",
|
||||
fill: "both",
|
||||
iterations: repeat + 1,
|
||||
direction: repeatType === "reverse" ? "alternate" : "normal",
|
||||
};
|
||||
if (pseudoElement)
|
||||
options.pseudoElement = pseudoElement;
|
||||
const animation = element.animate(keyframeOptions, options);
|
||||
if (statsBuffer.value) {
|
||||
animation.finished.finally(() => {
|
||||
activeAnimations.waapi--;
|
||||
});
|
||||
}
|
||||
return animation;
|
||||
}
|
||||
|
||||
export { startWaapiAnimation };
|
||||
13
node_modules/motion-dom/dist/es/animation/waapi/supports/partial-keyframes.mjs
generated
vendored
Normal file
13
node_modules/motion-dom/dist/es/animation/waapi/supports/partial-keyframes.mjs
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { memo } from 'motion-utils';
|
||||
|
||||
const supportsPartialKeyframes = /*@__PURE__*/ memo(() => {
|
||||
try {
|
||||
document.createElement("div").animate({ opacity: [1] });
|
||||
}
|
||||
catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
export { supportsPartialKeyframes };
|
||||
43
node_modules/motion-dom/dist/es/animation/waapi/supports/waapi.mjs
generated
vendored
Normal file
43
node_modules/motion-dom/dist/es/animation/waapi/supports/waapi.mjs
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
import { memo } from 'motion-utils';
|
||||
|
||||
/**
|
||||
* A list of values that can be hardware-accelerated.
|
||||
*/
|
||||
const acceleratedValues = new Set([
|
||||
"opacity",
|
||||
"clipPath",
|
||||
"filter",
|
||||
"transform",
|
||||
// TODO: Could be re-enabled now we have support for linear() easing
|
||||
// "background-color"
|
||||
]);
|
||||
const supportsWaapi = /*@__PURE__*/ memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
|
||||
function supportsBrowserAnimation(options) {
|
||||
const { motionValue, name, repeatDelay, repeatType, damping, type } = options;
|
||||
const subject = motionValue?.owner?.current;
|
||||
/**
|
||||
* We use this check instead of isHTMLElement() because we explicitly
|
||||
* **don't** want elements in different timing contexts (i.e. popups)
|
||||
* to be accelerated, as it's not possible to sync these animations
|
||||
* properly with those driven from the main window frameloop.
|
||||
*/
|
||||
if (!(subject instanceof HTMLElement)) {
|
||||
return false;
|
||||
}
|
||||
const { onUpdate, transformTemplate } = motionValue.owner.getProps();
|
||||
return (supportsWaapi() &&
|
||||
name &&
|
||||
acceleratedValues.has(name) &&
|
||||
(name !== "transform" || !transformTemplate) &&
|
||||
/**
|
||||
* If we're outputting values to onUpdate then we can't use WAAPI as there's
|
||||
* no way to read the value from WAAPI every frame.
|
||||
*/
|
||||
!onUpdate &&
|
||||
!repeatDelay &&
|
||||
repeatType !== "mirror" &&
|
||||
damping !== 0 &&
|
||||
type !== "inertia");
|
||||
}
|
||||
|
||||
export { supportsBrowserAnimation };
|
||||
14
node_modules/motion-dom/dist/es/animation/waapi/utils/accelerated-values.mjs
generated
vendored
Normal file
14
node_modules/motion-dom/dist/es/animation/waapi/utils/accelerated-values.mjs
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* A list of values that can be hardware-accelerated.
|
||||
*/
|
||||
const acceleratedValues = new Set([
|
||||
"opacity",
|
||||
"clipPath",
|
||||
"filter",
|
||||
"transform",
|
||||
// TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved
|
||||
// or until we implement support for linear() easing.
|
||||
// "background-color"
|
||||
]);
|
||||
|
||||
export { acceleratedValues };
|
||||
15
node_modules/motion-dom/dist/es/animation/waapi/utils/apply-generator.mjs
generated
vendored
Normal file
15
node_modules/motion-dom/dist/es/animation/waapi/utils/apply-generator.mjs
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { supportsLinearEasing } from '../../../utils/supports/linear-easing.mjs';
|
||||
import { isGenerator } from '../../generators/utils/is-generator.mjs';
|
||||
|
||||
function applyGeneratorOptions({ type, ...options }) {
|
||||
if (isGenerator(type) && supportsLinearEasing()) {
|
||||
return type.applyToOptions(options);
|
||||
}
|
||||
else {
|
||||
options.duration ?? (options.duration = 300);
|
||||
options.ease ?? (options.ease = "easeOut");
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
export { applyGeneratorOptions };
|
||||
12
node_modules/motion-dom/dist/es/animation/waapi/utils/linear.mjs
generated
vendored
Normal file
12
node_modules/motion-dom/dist/es/animation/waapi/utils/linear.mjs
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
const generateLinearEasing = (easing, duration, // as milliseconds
|
||||
resolution = 10 // as milliseconds
|
||||
) => {
|
||||
let points = "";
|
||||
const numPoints = Math.max(Math.round(duration / resolution), 2);
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
points += Math.round(easing(i / (numPoints - 1)) * 10000) / 10000 + ", ";
|
||||
}
|
||||
return `linear(${points.substring(0, points.length - 2)})`;
|
||||
};
|
||||
|
||||
export { generateLinearEasing };
|
||||
39
node_modules/motion-dom/dist/es/animation/waapi/utils/px-values.mjs
generated
vendored
Normal file
39
node_modules/motion-dom/dist/es/animation/waapi/utils/px-values.mjs
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
const pxValues = new Set([
|
||||
// Border props
|
||||
"borderWidth",
|
||||
"borderTopWidth",
|
||||
"borderRightWidth",
|
||||
"borderBottomWidth",
|
||||
"borderLeftWidth",
|
||||
"borderRadius",
|
||||
"radius",
|
||||
"borderTopLeftRadius",
|
||||
"borderTopRightRadius",
|
||||
"borderBottomRightRadius",
|
||||
"borderBottomLeftRadius",
|
||||
// Positioning props
|
||||
"width",
|
||||
"maxWidth",
|
||||
"height",
|
||||
"maxHeight",
|
||||
"top",
|
||||
"right",
|
||||
"bottom",
|
||||
"left",
|
||||
// Spacing props
|
||||
"padding",
|
||||
"paddingTop",
|
||||
"paddingRight",
|
||||
"paddingBottom",
|
||||
"paddingLeft",
|
||||
"margin",
|
||||
"marginTop",
|
||||
"marginRight",
|
||||
"marginBottom",
|
||||
"marginLeft",
|
||||
// Misc
|
||||
"backgroundPositionX",
|
||||
"backgroundPositionY",
|
||||
]);
|
||||
|
||||
export { pxValues };
|
||||
18
node_modules/motion-dom/dist/es/animation/waapi/utils/unsupported-easing.mjs
generated
vendored
Normal file
18
node_modules/motion-dom/dist/es/animation/waapi/utils/unsupported-easing.mjs
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import { anticipate, backInOut, circInOut } from 'motion-utils';
|
||||
|
||||
const unsupportedEasingFunctions = {
|
||||
anticipate,
|
||||
backInOut,
|
||||
circInOut,
|
||||
};
|
||||
function isUnsupportedEase(key) {
|
||||
return key in unsupportedEasingFunctions;
|
||||
}
|
||||
function replaceStringEasing(transition) {
|
||||
if (typeof transition.ease === "string" &&
|
||||
isUnsupportedEase(transition.ease)) {
|
||||
transition.ease = unsupportedEasingFunctions[transition.ease];
|
||||
}
|
||||
}
|
||||
|
||||
export { replaceStringEasing };
|
||||
47
node_modules/motion-dom/dist/es/effects/MotionValueState.mjs
generated
vendored
Normal file
47
node_modules/motion-dom/dist/es/effects/MotionValueState.mjs
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
import { frame, cancelFrame } from '../frameloop/frame.mjs';
|
||||
import { numberValueTypes } from '../value/types/maps/number.mjs';
|
||||
import { getValueAsType } from '../value/types/utils/get-as-type.mjs';
|
||||
|
||||
class MotionValueState {
|
||||
constructor() {
|
||||
this.latest = {};
|
||||
this.values = new Map();
|
||||
}
|
||||
set(name, value, render, computed, useDefaultValueType = true) {
|
||||
const existingValue = this.values.get(name);
|
||||
if (existingValue) {
|
||||
existingValue.onRemove();
|
||||
}
|
||||
const onChange = () => {
|
||||
const v = value.get();
|
||||
if (useDefaultValueType) {
|
||||
this.latest[name] = getValueAsType(v, numberValueTypes[name]);
|
||||
}
|
||||
else {
|
||||
this.latest[name] = v;
|
||||
}
|
||||
render && frame.render(render);
|
||||
};
|
||||
onChange();
|
||||
const cancelOnChange = value.on("change", onChange);
|
||||
computed && value.addDependent(computed);
|
||||
const remove = () => {
|
||||
cancelOnChange();
|
||||
render && cancelFrame(render);
|
||||
this.values.delete(name);
|
||||
computed && value.removeDependent(computed);
|
||||
};
|
||||
this.values.set(name, { value, onRemove: remove });
|
||||
return remove;
|
||||
}
|
||||
get(name) {
|
||||
return this.values.get(name)?.value;
|
||||
}
|
||||
destroy() {
|
||||
for (const value of this.values.values()) {
|
||||
value.onRemove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { MotionValueState };
|
||||
41
node_modules/motion-dom/dist/es/effects/attr/index.mjs
generated
vendored
Normal file
41
node_modules/motion-dom/dist/es/effects/attr/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
import { camelToDash } from '../../render/dom/utils/camel-to-dash.mjs';
|
||||
import { createSelectorEffect } from '../utils/create-dom-effect.mjs';
|
||||
import { createEffect } from '../utils/create-effect.mjs';
|
||||
|
||||
function canSetAsProperty(element, name) {
|
||||
if (!(name in element))
|
||||
return false;
|
||||
const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), name) ||
|
||||
Object.getOwnPropertyDescriptor(element, name);
|
||||
// Check if it has a setter
|
||||
return descriptor && typeof descriptor.set === "function";
|
||||
}
|
||||
const addAttrValue = (element, state, key, value) => {
|
||||
const isProp = canSetAsProperty(element, key);
|
||||
const name = isProp
|
||||
? key
|
||||
: key.startsWith("data") || key.startsWith("aria")
|
||||
? camelToDash(key)
|
||||
: key;
|
||||
/**
|
||||
* Set attribute directly via property if available
|
||||
*/
|
||||
const render = isProp
|
||||
? () => {
|
||||
element[name] = state.latest[key];
|
||||
}
|
||||
: () => {
|
||||
const v = state.latest[key];
|
||||
if (v === null || v === undefined) {
|
||||
element.removeAttribute(name);
|
||||
}
|
||||
else {
|
||||
element.setAttribute(name, String(v));
|
||||
}
|
||||
};
|
||||
return state.set(key, value, render);
|
||||
};
|
||||
const attrEffect = /*@__PURE__*/ createSelectorEffect(
|
||||
/*@__PURE__*/ createEffect(addAttrValue));
|
||||
|
||||
export { addAttrValue, attrEffect };
|
||||
9
node_modules/motion-dom/dist/es/effects/prop/index.mjs
generated
vendored
Normal file
9
node_modules/motion-dom/dist/es/effects/prop/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createEffect } from '../utils/create-effect.mjs';
|
||||
|
||||
const propEffect = /*@__PURE__*/ createEffect((subject, state, key, value) => {
|
||||
return state.set(key, value, () => {
|
||||
subject[key] = state.latest[key];
|
||||
}, undefined, false);
|
||||
});
|
||||
|
||||
export { propEffect };
|
||||
52
node_modules/motion-dom/dist/es/effects/style/index.mjs
generated
vendored
Normal file
52
node_modules/motion-dom/dist/es/effects/style/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
import { isCSSVar } from '../../render/dom/is-css-var.mjs';
|
||||
import { transformProps } from '../../render/utils/keys-transform.mjs';
|
||||
import { isHTMLElement } from '../../utils/is-html-element.mjs';
|
||||
import { MotionValue } from '../../value/index.mjs';
|
||||
import { createSelectorEffect } from '../utils/create-dom-effect.mjs';
|
||||
import { createEffect } from '../utils/create-effect.mjs';
|
||||
import { buildTransform } from './transform.mjs';
|
||||
|
||||
const originProps = new Set(["originX", "originY", "originZ"]);
|
||||
const addStyleValue = (element, state, key, value) => {
|
||||
let render = undefined;
|
||||
let computed = undefined;
|
||||
if (transformProps.has(key)) {
|
||||
if (!state.get("transform")) {
|
||||
// If this is an HTML element, we need to set the transform-box to fill-box
|
||||
// to normalise the transform relative to the element's bounding box
|
||||
if (!isHTMLElement(element) && !state.get("transformBox")) {
|
||||
addStyleValue(element, state, "transformBox", new MotionValue("fill-box"));
|
||||
}
|
||||
state.set("transform", new MotionValue("none"), () => {
|
||||
element.style.transform = buildTransform(state);
|
||||
});
|
||||
}
|
||||
computed = state.get("transform");
|
||||
}
|
||||
else if (originProps.has(key)) {
|
||||
if (!state.get("transformOrigin")) {
|
||||
state.set("transformOrigin", new MotionValue(""), () => {
|
||||
const originX = state.latest.originX ?? "50%";
|
||||
const originY = state.latest.originY ?? "50%";
|
||||
const originZ = state.latest.originZ ?? 0;
|
||||
element.style.transformOrigin = `${originX} ${originY} ${originZ}`;
|
||||
});
|
||||
}
|
||||
computed = state.get("transformOrigin");
|
||||
}
|
||||
else if (isCSSVar(key)) {
|
||||
render = () => {
|
||||
element.style.setProperty(key, state.latest[key]);
|
||||
};
|
||||
}
|
||||
else {
|
||||
render = () => {
|
||||
element.style[key] = state.latest[key];
|
||||
};
|
||||
}
|
||||
return state.set(key, value, render, computed);
|
||||
};
|
||||
const styleEffect = /*@__PURE__*/ createSelectorEffect(
|
||||
/*@__PURE__*/ createEffect(addStyleValue));
|
||||
|
||||
export { addStyleValue, styleEffect };
|
||||
38
node_modules/motion-dom/dist/es/effects/style/transform.mjs
generated
vendored
Normal file
38
node_modules/motion-dom/dist/es/effects/style/transform.mjs
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
import { transformPropOrder } from '../../render/utils/keys-transform.mjs';
|
||||
|
||||
const translateAlias = {
|
||||
x: "translateX",
|
||||
y: "translateY",
|
||||
z: "translateZ",
|
||||
transformPerspective: "perspective",
|
||||
};
|
||||
function buildTransform(state) {
|
||||
let transform = "";
|
||||
let transformIsDefault = true;
|
||||
/**
|
||||
* Loop over all possible transforms in order, adding the ones that
|
||||
* are present to the transform string.
|
||||
*/
|
||||
for (let i = 0; i < transformPropOrder.length; i++) {
|
||||
const key = transformPropOrder[i];
|
||||
const value = state.latest[key];
|
||||
if (value === undefined)
|
||||
continue;
|
||||
let valueIsDefault = true;
|
||||
if (typeof value === "number") {
|
||||
valueIsDefault = value === (key.startsWith("scale") ? 1 : 0);
|
||||
}
|
||||
else {
|
||||
valueIsDefault = parseFloat(value) === 0;
|
||||
}
|
||||
if (!valueIsDefault) {
|
||||
transformIsDefault = false;
|
||||
const transformName = translateAlias[key] || key;
|
||||
const valueToRender = state.latest[key];
|
||||
transform += `${transformName}(${valueToRender}) `;
|
||||
}
|
||||
}
|
||||
return transformIsDefault ? "none" : transform.trim();
|
||||
}
|
||||
|
||||
export { buildTransform };
|
||||
41
node_modules/motion-dom/dist/es/effects/svg/index.mjs
generated
vendored
Normal file
41
node_modules/motion-dom/dist/es/effects/svg/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
import { MotionValue } from '../../value/index.mjs';
|
||||
import { px } from '../../value/types/numbers/units.mjs';
|
||||
import { addAttrValue } from '../attr/index.mjs';
|
||||
import { addStyleValue } from '../style/index.mjs';
|
||||
import { createSelectorEffect } from '../utils/create-dom-effect.mjs';
|
||||
import { createEffect } from '../utils/create-effect.mjs';
|
||||
import { frame } from '../../frameloop/frame.mjs';
|
||||
|
||||
const toPx = px.transform;
|
||||
function addSVGPathValue(element, state, key, value) {
|
||||
frame.render(() => element.setAttribute("pathLength", "1"));
|
||||
if (key === "pathOffset") {
|
||||
return state.set(key, value, () => element.setAttribute("stroke-dashoffset", toPx(-state.latest[key])));
|
||||
}
|
||||
else {
|
||||
if (!state.get("stroke-dasharray")) {
|
||||
state.set("stroke-dasharray", new MotionValue("1 1"), () => {
|
||||
const { pathLength = 1, pathSpacing } = state.latest;
|
||||
element.setAttribute("stroke-dasharray", `${toPx(pathLength)} ${toPx(pathSpacing ?? 1 - Number(pathLength))}`);
|
||||
});
|
||||
}
|
||||
return state.set(key, value, undefined, state.get("stroke-dasharray"));
|
||||
}
|
||||
}
|
||||
const addSVGValue = (element, state, key, value) => {
|
||||
if (key.startsWith("path")) {
|
||||
return addSVGPathValue(element, state, key, value);
|
||||
}
|
||||
else if (key.startsWith("attr")) {
|
||||
return addAttrValue(element, state, convertAttrKey(key), value);
|
||||
}
|
||||
const handler = key in element.style ? addStyleValue : addAttrValue;
|
||||
return handler(element, state, key, value);
|
||||
};
|
||||
const svgEffect = /*@__PURE__*/ createSelectorEffect(
|
||||
/*@__PURE__*/ createEffect(addSVGValue));
|
||||
function convertAttrKey(key) {
|
||||
return key.replace(/^attr([A-Z])/, (_, firstChar) => firstChar.toLowerCase());
|
||||
}
|
||||
|
||||
export { svgEffect };
|
||||
18
node_modules/motion-dom/dist/es/effects/utils/create-dom-effect.mjs
generated
vendored
Normal file
18
node_modules/motion-dom/dist/es/effects/utils/create-dom-effect.mjs
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import { resolveElements } from '../../utils/resolve-elements.mjs';
|
||||
|
||||
function createSelectorEffect(subjectEffect) {
|
||||
return (subject, values) => {
|
||||
const elements = resolveElements(subject);
|
||||
const subscriptions = [];
|
||||
for (const element of elements) {
|
||||
const remove = subjectEffect(element, values);
|
||||
subscriptions.push(remove);
|
||||
}
|
||||
return () => {
|
||||
for (const remove of subscriptions)
|
||||
remove();
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export { createSelectorEffect };
|
||||
21
node_modules/motion-dom/dist/es/effects/utils/create-effect.mjs
generated
vendored
Normal file
21
node_modules/motion-dom/dist/es/effects/utils/create-effect.mjs
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import { MotionValueState } from '../MotionValueState.mjs';
|
||||
|
||||
function createEffect(addValue) {
|
||||
const stateCache = new WeakMap();
|
||||
const subscriptions = [];
|
||||
return (subject, values) => {
|
||||
const state = stateCache.get(subject) ?? new MotionValueState();
|
||||
stateCache.set(subject, state);
|
||||
for (const key in values) {
|
||||
const value = values[key];
|
||||
const remove = addValue(subject, state, key, value);
|
||||
subscriptions.push(remove);
|
||||
}
|
||||
return () => {
|
||||
for (const cancel of subscriptions)
|
||||
cancel();
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export { createEffect };
|
||||
71
node_modules/motion-dom/dist/es/frameloop/batcher.mjs
generated
vendored
Normal file
71
node_modules/motion-dom/dist/es/frameloop/batcher.mjs
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
import { MotionGlobalConfig } from 'motion-utils';
|
||||
import { stepsOrder } from './order.mjs';
|
||||
import { createRenderStep } from './render-step.mjs';
|
||||
|
||||
const maxElapsed = 40;
|
||||
function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
|
||||
let runNextFrame = false;
|
||||
let useDefaultElapsed = true;
|
||||
const state = {
|
||||
delta: 0.0,
|
||||
timestamp: 0.0,
|
||||
isProcessing: false,
|
||||
};
|
||||
const flagRunNextFrame = () => (runNextFrame = true);
|
||||
const steps = stepsOrder.reduce((acc, key) => {
|
||||
acc[key] = createRenderStep(flagRunNextFrame, allowKeepAlive ? key : undefined);
|
||||
return acc;
|
||||
}, {});
|
||||
const { setup, read, resolveKeyframes, preUpdate, update, preRender, render, postRender, } = steps;
|
||||
const processBatch = () => {
|
||||
const timestamp = MotionGlobalConfig.useManualTiming
|
||||
? state.timestamp
|
||||
: performance.now();
|
||||
runNextFrame = false;
|
||||
if (!MotionGlobalConfig.useManualTiming) {
|
||||
state.delta = useDefaultElapsed
|
||||
? 1000 / 60
|
||||
: Math.max(Math.min(timestamp - state.timestamp, maxElapsed), 1);
|
||||
}
|
||||
state.timestamp = timestamp;
|
||||
state.isProcessing = true;
|
||||
// Unrolled render loop for better per-frame performance
|
||||
setup.process(state);
|
||||
read.process(state);
|
||||
resolveKeyframes.process(state);
|
||||
preUpdate.process(state);
|
||||
update.process(state);
|
||||
preRender.process(state);
|
||||
render.process(state);
|
||||
postRender.process(state);
|
||||
state.isProcessing = false;
|
||||
if (runNextFrame && allowKeepAlive) {
|
||||
useDefaultElapsed = false;
|
||||
scheduleNextBatch(processBatch);
|
||||
}
|
||||
};
|
||||
const wake = () => {
|
||||
runNextFrame = true;
|
||||
useDefaultElapsed = true;
|
||||
if (!state.isProcessing) {
|
||||
scheduleNextBatch(processBatch);
|
||||
}
|
||||
};
|
||||
const schedule = stepsOrder.reduce((acc, key) => {
|
||||
const step = steps[key];
|
||||
acc[key] = (process, keepAlive = false, immediate = false) => {
|
||||
if (!runNextFrame)
|
||||
wake();
|
||||
return step.schedule(process, keepAlive, immediate);
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
const cancel = (process) => {
|
||||
for (let i = 0; i < stepsOrder.length; i++) {
|
||||
steps[stepsOrder[i]].cancel(process);
|
||||
}
|
||||
};
|
||||
return { schedule, cancel, state, steps };
|
||||
}
|
||||
|
||||
export { createRenderBatcher };
|
||||
6
node_modules/motion-dom/dist/es/frameloop/frame.mjs
generated
vendored
Normal file
6
node_modules/motion-dom/dist/es/frameloop/frame.mjs
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import { noop } from 'motion-utils';
|
||||
import { createRenderBatcher } from './batcher.mjs';
|
||||
|
||||
const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps, } = /* @__PURE__ */ createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop, true);
|
||||
|
||||
export { cancelFrame, frame, frameData, frameSteps };
|
||||
20
node_modules/motion-dom/dist/es/frameloop/index-legacy.mjs
generated
vendored
Normal file
20
node_modules/motion-dom/dist/es/frameloop/index-legacy.mjs
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import { stepsOrder } from './order.mjs';
|
||||
import { frame, cancelFrame } from './frame.mjs';
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* Import as `frame` instead.
|
||||
*/
|
||||
const sync = frame;
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* Use cancelFrame(callback) instead.
|
||||
*/
|
||||
const cancelSync = stepsOrder.reduce((acc, key) => {
|
||||
acc[key] = (process) => cancelFrame(process);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
export { cancelSync, sync };
|
||||
6
node_modules/motion-dom/dist/es/frameloop/microtask.mjs
generated
vendored
Normal file
6
node_modules/motion-dom/dist/es/frameloop/microtask.mjs
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import { createRenderBatcher } from './batcher.mjs';
|
||||
|
||||
const { schedule: microtask, cancel: cancelMicrotask } =
|
||||
/* @__PURE__ */ createRenderBatcher(queueMicrotask, false);
|
||||
|
||||
export { cancelMicrotask, microtask };
|
||||
12
node_modules/motion-dom/dist/es/frameloop/order.mjs
generated
vendored
Normal file
12
node_modules/motion-dom/dist/es/frameloop/order.mjs
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
const stepsOrder = [
|
||||
"setup", // Compute
|
||||
"read", // Read
|
||||
"resolveKeyframes", // Write/Read/Write/Read
|
||||
"preUpdate", // Compute
|
||||
"update", // Compute
|
||||
"preRender", // Compute
|
||||
"render", // Write
|
||||
"postRender", // Compute
|
||||
];
|
||||
|
||||
export { stepsOrder };
|
||||
92
node_modules/motion-dom/dist/es/frameloop/render-step.mjs
generated
vendored
Normal file
92
node_modules/motion-dom/dist/es/frameloop/render-step.mjs
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
import { statsBuffer } from '../stats/buffer.mjs';
|
||||
|
||||
function createRenderStep(runNextFrame, stepName) {
|
||||
/**
|
||||
* We create and reuse two queues, one to queue jobs for the current frame
|
||||
* and one for the next. We reuse to avoid triggering GC after x frames.
|
||||
*/
|
||||
let thisFrame = new Set();
|
||||
let nextFrame = new Set();
|
||||
/**
|
||||
* Track whether we're currently processing jobs in this step. This way
|
||||
* we can decide whether to schedule new jobs for this frame or next.
|
||||
*/
|
||||
let isProcessing = false;
|
||||
let flushNextFrame = false;
|
||||
/**
|
||||
* A set of processes which were marked keepAlive when scheduled.
|
||||
*/
|
||||
const toKeepAlive = new WeakSet();
|
||||
let latestFrameData = {
|
||||
delta: 0.0,
|
||||
timestamp: 0.0,
|
||||
isProcessing: false,
|
||||
};
|
||||
let numCalls = 0;
|
||||
function triggerCallback(callback) {
|
||||
if (toKeepAlive.has(callback)) {
|
||||
step.schedule(callback);
|
||||
runNextFrame();
|
||||
}
|
||||
numCalls++;
|
||||
callback(latestFrameData);
|
||||
}
|
||||
const step = {
|
||||
/**
|
||||
* Schedule a process to run on the next frame.
|
||||
*/
|
||||
schedule: (callback, keepAlive = false, immediate = false) => {
|
||||
const addToCurrentFrame = immediate && isProcessing;
|
||||
const queue = addToCurrentFrame ? thisFrame : nextFrame;
|
||||
if (keepAlive)
|
||||
toKeepAlive.add(callback);
|
||||
if (!queue.has(callback))
|
||||
queue.add(callback);
|
||||
return callback;
|
||||
},
|
||||
/**
|
||||
* Cancel the provided callback from running on the next frame.
|
||||
*/
|
||||
cancel: (callback) => {
|
||||
nextFrame.delete(callback);
|
||||
toKeepAlive.delete(callback);
|
||||
},
|
||||
/**
|
||||
* Execute all schedule callbacks.
|
||||
*/
|
||||
process: (frameData) => {
|
||||
latestFrameData = frameData;
|
||||
/**
|
||||
* If we're already processing we've probably been triggered by a flushSync
|
||||
* inside an existing process. Instead of executing, mark flushNextFrame
|
||||
* as true and ensure we flush the following frame at the end of this one.
|
||||
*/
|
||||
if (isProcessing) {
|
||||
flushNextFrame = true;
|
||||
return;
|
||||
}
|
||||
isProcessing = true;
|
||||
[thisFrame, nextFrame] = [nextFrame, thisFrame];
|
||||
// Execute this frame
|
||||
thisFrame.forEach(triggerCallback);
|
||||
/**
|
||||
* If we're recording stats then
|
||||
*/
|
||||
if (stepName && statsBuffer.value) {
|
||||
statsBuffer.value.frameloop[stepName].push(numCalls);
|
||||
}
|
||||
numCalls = 0;
|
||||
// Clear the frame so no callbacks remain. This is to avoid
|
||||
// memory leaks should this render step not run for a while.
|
||||
thisFrame.clear();
|
||||
isProcessing = false;
|
||||
if (flushNextFrame) {
|
||||
flushNextFrame = false;
|
||||
step.process(frameData);
|
||||
}
|
||||
},
|
||||
};
|
||||
return step;
|
||||
}
|
||||
|
||||
export { createRenderStep };
|
||||
31
node_modules/motion-dom/dist/es/frameloop/sync-time.mjs
generated
vendored
Normal file
31
node_modules/motion-dom/dist/es/frameloop/sync-time.mjs
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import { MotionGlobalConfig } from 'motion-utils';
|
||||
import { frameData } from './frame.mjs';
|
||||
|
||||
let now;
|
||||
function clearTime() {
|
||||
now = undefined;
|
||||
}
|
||||
/**
|
||||
* An eventloop-synchronous alternative to performance.now().
|
||||
*
|
||||
* Ensures that time measurements remain consistent within a synchronous context.
|
||||
* Usually calling performance.now() twice within the same synchronous context
|
||||
* will return different values which isn't useful for animations when we're usually
|
||||
* trying to sync animations to the same frame.
|
||||
*/
|
||||
const time = {
|
||||
now: () => {
|
||||
if (now === undefined) {
|
||||
time.set(frameData.isProcessing || MotionGlobalConfig.useManualTiming
|
||||
? frameData.timestamp
|
||||
: performance.now());
|
||||
}
|
||||
return now;
|
||||
},
|
||||
set: (newTime) => {
|
||||
now = newTime;
|
||||
queueMicrotask(clearTime);
|
||||
},
|
||||
};
|
||||
|
||||
export { time };
|
||||
9
node_modules/motion-dom/dist/es/gestures/drag/state/is-active.mjs
generated
vendored
Normal file
9
node_modules/motion-dom/dist/es/gestures/drag/state/is-active.mjs
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
const isDragging = {
|
||||
x: false,
|
||||
y: false,
|
||||
};
|
||||
function isDragActive() {
|
||||
return isDragging.x || isDragging.y;
|
||||
}
|
||||
|
||||
export { isDragActive, isDragging };
|
||||
28
node_modules/motion-dom/dist/es/gestures/drag/state/set-active.mjs
generated
vendored
Normal file
28
node_modules/motion-dom/dist/es/gestures/drag/state/set-active.mjs
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
import { isDragging } from './is-active.mjs';
|
||||
|
||||
function setDragLock(axis) {
|
||||
if (axis === "x" || axis === "y") {
|
||||
if (isDragging[axis]) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
isDragging[axis] = true;
|
||||
return () => {
|
||||
isDragging[axis] = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (isDragging.x || isDragging.y) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
isDragging.x = isDragging.y = true;
|
||||
return () => {
|
||||
isDragging.x = isDragging.y = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { setDragLock };
|
||||
37
node_modules/motion-dom/dist/es/gestures/hover.mjs
generated
vendored
Normal file
37
node_modules/motion-dom/dist/es/gestures/hover.mjs
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
import { isDragActive } from './drag/state/is-active.mjs';
|
||||
import { setupGesture } from './utils/setup.mjs';
|
||||
|
||||
function isValidHover(event) {
|
||||
return !(event.pointerType === "touch" || isDragActive());
|
||||
}
|
||||
/**
|
||||
* Create a hover gesture. hover() is different to .addEventListener("pointerenter")
|
||||
* in that it has an easier syntax, filters out polyfilled touch events, interoperates
|
||||
* with drag gestures, and automatically removes the "pointerennd" event listener when the hover ends.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
function hover(elementOrSelector, onHoverStart, options = {}) {
|
||||
const [elements, eventOptions, cancel] = setupGesture(elementOrSelector, options);
|
||||
const onPointerEnter = (enterEvent) => {
|
||||
if (!isValidHover(enterEvent))
|
||||
return;
|
||||
const { target } = enterEvent;
|
||||
const onHoverEnd = onHoverStart(target, enterEvent);
|
||||
if (typeof onHoverEnd !== "function" || !target)
|
||||
return;
|
||||
const onPointerLeave = (leaveEvent) => {
|
||||
if (!isValidHover(leaveEvent))
|
||||
return;
|
||||
onHoverEnd(leaveEvent);
|
||||
target.removeEventListener("pointerleave", onPointerLeave);
|
||||
};
|
||||
target.addEventListener("pointerleave", onPointerLeave, eventOptions);
|
||||
};
|
||||
elements.forEach((element) => {
|
||||
element.addEventListener("pointerenter", onPointerEnter, eventOptions);
|
||||
});
|
||||
return cancel;
|
||||
}
|
||||
|
||||
export { hover };
|
||||
83
node_modules/motion-dom/dist/es/gestures/press/index.mjs
generated
vendored
Normal file
83
node_modules/motion-dom/dist/es/gestures/press/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
import { isHTMLElement } from '../../utils/is-html-element.mjs';
|
||||
import { isDragActive } from '../drag/state/is-active.mjs';
|
||||
import { isNodeOrChild } from '../utils/is-node-or-child.mjs';
|
||||
import { isPrimaryPointer } from '../utils/is-primary-pointer.mjs';
|
||||
import { setupGesture } from '../utils/setup.mjs';
|
||||
import { isElementKeyboardAccessible } from './utils/is-keyboard-accessible.mjs';
|
||||
import { enableKeyboardPress } from './utils/keyboard.mjs';
|
||||
import { isPressing } from './utils/state.mjs';
|
||||
|
||||
/**
|
||||
* Filter out events that are not primary pointer events, or are triggering
|
||||
* while a Motion gesture is active.
|
||||
*/
|
||||
function isValidPressEvent(event) {
|
||||
return isPrimaryPointer(event) && !isDragActive();
|
||||
}
|
||||
/**
|
||||
* Create a press gesture.
|
||||
*
|
||||
* Press is different to `"pointerdown"`, `"pointerup"` in that it
|
||||
* automatically filters out secondary pointer events like right
|
||||
* click and multitouch.
|
||||
*
|
||||
* It also adds accessibility support for keyboards, where
|
||||
* an element with a press gesture will receive focus and
|
||||
* trigger on Enter `"keydown"` and `"keyup"` events.
|
||||
*
|
||||
* This is different to a browser's `"click"` event, which does
|
||||
* respond to keyboards but only for the `"click"` itself, rather
|
||||
* than the press start and end/cancel. The element also needs
|
||||
* to be focusable for this to work, whereas a press gesture will
|
||||
* make an element focusable by default.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
function press(targetOrSelector, onPressStart, options = {}) {
|
||||
const [targets, eventOptions, cancelEvents] = setupGesture(targetOrSelector, options);
|
||||
const startPress = (startEvent) => {
|
||||
const target = startEvent.currentTarget;
|
||||
if (!isValidPressEvent(startEvent))
|
||||
return;
|
||||
isPressing.add(target);
|
||||
const onPressEnd = onPressStart(target, startEvent);
|
||||
const onPointerEnd = (endEvent, success) => {
|
||||
window.removeEventListener("pointerup", onPointerUp);
|
||||
window.removeEventListener("pointercancel", onPointerCancel);
|
||||
if (isPressing.has(target)) {
|
||||
isPressing.delete(target);
|
||||
}
|
||||
if (!isValidPressEvent(endEvent)) {
|
||||
return;
|
||||
}
|
||||
if (typeof onPressEnd === "function") {
|
||||
onPressEnd(endEvent, { success });
|
||||
}
|
||||
};
|
||||
const onPointerUp = (upEvent) => {
|
||||
onPointerEnd(upEvent, target === window ||
|
||||
target === document ||
|
||||
options.useGlobalTarget ||
|
||||
isNodeOrChild(target, upEvent.target));
|
||||
};
|
||||
const onPointerCancel = (cancelEvent) => {
|
||||
onPointerEnd(cancelEvent, false);
|
||||
};
|
||||
window.addEventListener("pointerup", onPointerUp, eventOptions);
|
||||
window.addEventListener("pointercancel", onPointerCancel, eventOptions);
|
||||
};
|
||||
targets.forEach((target) => {
|
||||
const pointerDownTarget = options.useGlobalTarget ? window : target;
|
||||
pointerDownTarget.addEventListener("pointerdown", startPress, eventOptions);
|
||||
if (isHTMLElement(target)) {
|
||||
target.addEventListener("focus", (event) => enableKeyboardPress(event, eventOptions));
|
||||
if (!isElementKeyboardAccessible(target) &&
|
||||
!target.hasAttribute("tabindex")) {
|
||||
target.tabIndex = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
return cancelEvents;
|
||||
}
|
||||
|
||||
export { press };
|
||||
13
node_modules/motion-dom/dist/es/gestures/press/utils/is-keyboard-accessible.mjs
generated
vendored
Normal file
13
node_modules/motion-dom/dist/es/gestures/press/utils/is-keyboard-accessible.mjs
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
const focusableElements = new Set([
|
||||
"BUTTON",
|
||||
"INPUT",
|
||||
"SELECT",
|
||||
"TEXTAREA",
|
||||
"A",
|
||||
]);
|
||||
function isElementKeyboardAccessible(element) {
|
||||
return (focusableElements.has(element.tagName) ||
|
||||
element.tabIndex !== -1);
|
||||
}
|
||||
|
||||
export { isElementKeyboardAccessible };
|
||||
38
node_modules/motion-dom/dist/es/gestures/press/utils/keyboard.mjs
generated
vendored
Normal file
38
node_modules/motion-dom/dist/es/gestures/press/utils/keyboard.mjs
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
import { isPressing } from './state.mjs';
|
||||
|
||||
/**
|
||||
* Filter out events that are not "Enter" keys.
|
||||
*/
|
||||
function filterEvents(callback) {
|
||||
return (event) => {
|
||||
if (event.key !== "Enter")
|
||||
return;
|
||||
callback(event);
|
||||
};
|
||||
}
|
||||
function firePointerEvent(target, type) {
|
||||
target.dispatchEvent(new PointerEvent("pointer" + type, { isPrimary: true, bubbles: true }));
|
||||
}
|
||||
const enableKeyboardPress = (focusEvent, eventOptions) => {
|
||||
const element = focusEvent.currentTarget;
|
||||
if (!element)
|
||||
return;
|
||||
const handleKeydown = filterEvents(() => {
|
||||
if (isPressing.has(element))
|
||||
return;
|
||||
firePointerEvent(element, "down");
|
||||
const handleKeyup = filterEvents(() => {
|
||||
firePointerEvent(element, "up");
|
||||
});
|
||||
const handleBlur = () => firePointerEvent(element, "cancel");
|
||||
element.addEventListener("keyup", handleKeyup, eventOptions);
|
||||
element.addEventListener("blur", handleBlur, eventOptions);
|
||||
});
|
||||
element.addEventListener("keydown", handleKeydown, eventOptions);
|
||||
/**
|
||||
* Add an event listener that fires on blur to remove the keydown events.
|
||||
*/
|
||||
element.addEventListener("blur", () => element.removeEventListener("keydown", handleKeydown), eventOptions);
|
||||
};
|
||||
|
||||
export { enableKeyboardPress };
|
||||
3
node_modules/motion-dom/dist/es/gestures/press/utils/state.mjs
generated
vendored
Normal file
3
node_modules/motion-dom/dist/es/gestures/press/utils/state.mjs
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
const isPressing = new WeakSet();
|
||||
|
||||
export { isPressing };
|
||||
20
node_modules/motion-dom/dist/es/gestures/utils/is-node-or-child.mjs
generated
vendored
Normal file
20
node_modules/motion-dom/dist/es/gestures/utils/is-node-or-child.mjs
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Recursively traverse up the tree to check whether the provided child node
|
||||
* is the parent or a descendant of it.
|
||||
*
|
||||
* @param parent - Element to find
|
||||
* @param child - Element to test against parent
|
||||
*/
|
||||
const isNodeOrChild = (parent, child) => {
|
||||
if (!child) {
|
||||
return false;
|
||||
}
|
||||
else if (parent === child) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return isNodeOrChild(parent, child.parentElement);
|
||||
}
|
||||
};
|
||||
|
||||
export { isNodeOrChild };
|
||||
18
node_modules/motion-dom/dist/es/gestures/utils/is-primary-pointer.mjs
generated
vendored
Normal file
18
node_modules/motion-dom/dist/es/gestures/utils/is-primary-pointer.mjs
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
const isPrimaryPointer = (event) => {
|
||||
if (event.pointerType === "mouse") {
|
||||
return typeof event.button !== "number" || event.button <= 0;
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* isPrimary is true for all mice buttons, whereas every touch point
|
||||
* is regarded as its own input. So subsequent concurrent touch points
|
||||
* will be false.
|
||||
*
|
||||
* Specifically match against false here as incomplete versions of
|
||||
* PointerEvents in very old browser might have it set as undefined.
|
||||
*/
|
||||
return event.isPrimary !== false;
|
||||
}
|
||||
};
|
||||
|
||||
export { isPrimaryPointer };
|
||||
15
node_modules/motion-dom/dist/es/gestures/utils/setup.mjs
generated
vendored
Normal file
15
node_modules/motion-dom/dist/es/gestures/utils/setup.mjs
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { resolveElements } from '../../utils/resolve-elements.mjs';
|
||||
|
||||
function setupGesture(elementOrSelector, options) {
|
||||
const elements = resolveElements(elementOrSelector);
|
||||
const gestureAbortController = new AbortController();
|
||||
const eventOptions = {
|
||||
passive: true,
|
||||
...options,
|
||||
signal: gestureAbortController.signal,
|
||||
};
|
||||
const cancel = () => gestureAbortController.abort();
|
||||
return [elements, eventOptions, cancel];
|
||||
}
|
||||
|
||||
export { setupGesture };
|
||||
100
node_modules/motion-dom/dist/es/index.mjs
generated
vendored
Normal file
100
node_modules/motion-dom/dist/es/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
export { AsyncMotionValueAnimation } from './animation/AsyncMotionValueAnimation.mjs';
|
||||
export { GroupAnimation } from './animation/GroupAnimation.mjs';
|
||||
export { GroupAnimationWithThen } from './animation/GroupAnimationWithThen.mjs';
|
||||
export { JSAnimation, animateValue } from './animation/JSAnimation.mjs';
|
||||
export { NativeAnimation } from './animation/NativeAnimation.mjs';
|
||||
export { NativeAnimationExtended } from './animation/NativeAnimationExtended.mjs';
|
||||
export { NativeAnimationWrapper } from './animation/NativeAnimationWrapper.mjs';
|
||||
export { animationMapKey, getAnimationMap } from './animation/utils/active-animations.mjs';
|
||||
export { getVariableValue, parseCSSVariable } from './animation/utils/css-variables-conversion.mjs';
|
||||
export { getValueTransition } from './animation/utils/get-value-transition.mjs';
|
||||
export { isCSSVariableName, isCSSVariableToken } from './animation/utils/is-css-variable.mjs';
|
||||
export { makeAnimationInstant } from './animation/utils/make-animation-instant.mjs';
|
||||
export { inertia } from './animation/generators/inertia.mjs';
|
||||
export { defaultEasing, keyframes } from './animation/generators/keyframes.mjs';
|
||||
export { spring } from './animation/generators/spring/index.mjs';
|
||||
export { calcGeneratorDuration, maxGeneratorDuration } from './animation/generators/utils/calc-duration.mjs';
|
||||
export { createGeneratorEasing } from './animation/generators/utils/create-generator-easing.mjs';
|
||||
export { isGenerator } from './animation/generators/utils/is-generator.mjs';
|
||||
export { DOMKeyframesResolver } from './animation/keyframes/DOMKeyframesResolver.mjs';
|
||||
export { KeyframeResolver, flushKeyframeResolvers } from './animation/keyframes/KeyframesResolver.mjs';
|
||||
export { defaultOffset } from './animation/keyframes/offsets/default.mjs';
|
||||
export { fillOffset } from './animation/keyframes/offsets/fill.mjs';
|
||||
export { convertOffsetToTimes } from './animation/keyframes/offsets/time.mjs';
|
||||
export { applyPxDefaults } from './animation/keyframes/utils/apply-px-defaults.mjs';
|
||||
export { fillWildcards } from './animation/keyframes/utils/fill-wildcards.mjs';
|
||||
export { cubicBezierAsString } from './animation/waapi/easing/cubic-bezier.mjs';
|
||||
export { isWaapiSupportedEasing } from './animation/waapi/easing/is-supported.mjs';
|
||||
export { mapEasingToNativeEasing } from './animation/waapi/easing/map-easing.mjs';
|
||||
export { supportedWaapiEasing } from './animation/waapi/easing/supported.mjs';
|
||||
export { startWaapiAnimation } from './animation/waapi/start-waapi-animation.mjs';
|
||||
export { supportsPartialKeyframes } from './animation/waapi/supports/partial-keyframes.mjs';
|
||||
export { supportsBrowserAnimation } from './animation/waapi/supports/waapi.mjs';
|
||||
export { acceleratedValues } from './animation/waapi/utils/accelerated-values.mjs';
|
||||
export { applyGeneratorOptions } from './animation/waapi/utils/apply-generator.mjs';
|
||||
export { generateLinearEasing } from './animation/waapi/utils/linear.mjs';
|
||||
export { addAttrValue, attrEffect } from './effects/attr/index.mjs';
|
||||
export { propEffect } from './effects/prop/index.mjs';
|
||||
export { addStyleValue, styleEffect } from './effects/style/index.mjs';
|
||||
export { svgEffect } from './effects/svg/index.mjs';
|
||||
export { createRenderBatcher } from './frameloop/batcher.mjs';
|
||||
export { cancelMicrotask, microtask } from './frameloop/microtask.mjs';
|
||||
export { time } from './frameloop/sync-time.mjs';
|
||||
export { isDragActive, isDragging } from './gestures/drag/state/is-active.mjs';
|
||||
export { setDragLock } from './gestures/drag/state/set-active.mjs';
|
||||
export { hover } from './gestures/hover.mjs';
|
||||
export { press } from './gestures/press/index.mjs';
|
||||
export { isNodeOrChild } from './gestures/utils/is-node-or-child.mjs';
|
||||
export { isPrimaryPointer } from './gestures/utils/is-primary-pointer.mjs';
|
||||
export { defaultTransformValue, parseValueFromTransform, readTransformValue } from './render/dom/parse-transform.mjs';
|
||||
export { getComputedStyle } from './render/dom/style-computed.mjs';
|
||||
export { setStyle } from './render/dom/style-set.mjs';
|
||||
export { positionalKeys } from './render/utils/keys-position.mjs';
|
||||
export { transformPropOrder, transformProps } from './render/utils/keys-transform.mjs';
|
||||
export { resize } from './resize/index.mjs';
|
||||
export { observeTimeline } from './scroll/observe.mjs';
|
||||
export { recordStats } from './stats/index.mjs';
|
||||
export { activeAnimations } from './stats/animation-count.mjs';
|
||||
export { statsBuffer } from './stats/buffer.mjs';
|
||||
export { interpolate } from './utils/interpolate.mjs';
|
||||
export { isHTMLElement } from './utils/is-html-element.mjs';
|
||||
export { isSVGElement } from './utils/is-svg-element.mjs';
|
||||
export { isSVGSVGElement } from './utils/is-svg-svg-element.mjs';
|
||||
export { mix } from './utils/mix/index.mjs';
|
||||
export { mixColor, mixLinearColor } from './utils/mix/color.mjs';
|
||||
export { getMixer, mixArray, mixComplex, mixObject } from './utils/mix/complex.mjs';
|
||||
export { mixImmediate } from './utils/mix/immediate.mjs';
|
||||
export { mixNumber } from './utils/mix/number.mjs';
|
||||
export { invisibleValues, mixVisibility } from './utils/mix/visibility.mjs';
|
||||
export { resolveElements } from './utils/resolve-elements.mjs';
|
||||
export { getOriginIndex, stagger } from './utils/stagger.mjs';
|
||||
export { supportsFlags } from './utils/supports/flags.mjs';
|
||||
export { supportsLinearEasing } from './utils/supports/linear-easing.mjs';
|
||||
export { supportsScrollTimeline } from './utils/supports/scroll-timeline.mjs';
|
||||
export { transform } from './utils/transform.mjs';
|
||||
export { MotionValue, collectMotionValues, motionValue } from './value/index.mjs';
|
||||
export { mapValue } from './value/map-value.mjs';
|
||||
export { attachSpring, springValue } from './value/spring-value.mjs';
|
||||
export { transformValue } from './value/transform-value.mjs';
|
||||
export { color } from './value/types/color/index.mjs';
|
||||
export { hex } from './value/types/color/hex.mjs';
|
||||
export { hsla } from './value/types/color/hsla.mjs';
|
||||
export { hslaToRgba } from './value/types/color/hsla-to-rgba.mjs';
|
||||
export { rgbUnit, rgba } from './value/types/color/rgba.mjs';
|
||||
export { analyseComplexValue, complex } from './value/types/complex/index.mjs';
|
||||
export { dimensionValueTypes, findDimensionValueType } from './value/types/dimensions.mjs';
|
||||
export { defaultValueTypes, getDefaultValueType } from './value/types/maps/defaults.mjs';
|
||||
export { numberValueTypes } from './value/types/maps/number.mjs';
|
||||
export { transformValueTypes } from './value/types/maps/transform.mjs';
|
||||
export { alpha, number, scale } from './value/types/numbers/index.mjs';
|
||||
export { degrees, percent, progressPercentage, px, vh, vw } from './value/types/numbers/units.mjs';
|
||||
export { testValueType } from './value/types/test.mjs';
|
||||
export { getAnimatableNone } from './value/types/utils/animatable-none.mjs';
|
||||
export { findValueType } from './value/types/utils/find.mjs';
|
||||
export { getValueAsType } from './value/types/utils/get-as-type.mjs';
|
||||
export { isMotionValue } from './value/utils/is-motion-value.mjs';
|
||||
export { ViewTransitionBuilder, animateView } from './view/index.mjs';
|
||||
export { getViewAnimationLayerInfo } from './view/utils/get-layer-info.mjs';
|
||||
export { getViewAnimations } from './view/utils/get-view-animations.mjs';
|
||||
export { cancelSync, sync } from './frameloop/index-legacy.mjs';
|
||||
export { cancelFrame, frame, frameData, frameSteps } from './frameloop/frame.mjs';
|
||||
3
node_modules/motion-dom/dist/es/render/dom/is-css-var.mjs
generated
vendored
Normal file
3
node_modules/motion-dom/dist/es/render/dom/is-css-var.mjs
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
const isCSSVar = (name) => name.startsWith("--");
|
||||
|
||||
export { isCSSVar };
|
||||
83
node_modules/motion-dom/dist/es/render/dom/parse-transform.mjs
generated
vendored
Normal file
83
node_modules/motion-dom/dist/es/render/dom/parse-transform.mjs
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
const radToDeg = (rad) => (rad * 180) / Math.PI;
|
||||
const rotate = (v) => {
|
||||
const angle = radToDeg(Math.atan2(v[1], v[0]));
|
||||
return rebaseAngle(angle);
|
||||
};
|
||||
const matrix2dParsers = {
|
||||
x: 4,
|
||||
y: 5,
|
||||
translateX: 4,
|
||||
translateY: 5,
|
||||
scaleX: 0,
|
||||
scaleY: 3,
|
||||
scale: (v) => (Math.abs(v[0]) + Math.abs(v[3])) / 2,
|
||||
rotate,
|
||||
rotateZ: rotate,
|
||||
skewX: (v) => radToDeg(Math.atan(v[1])),
|
||||
skewY: (v) => radToDeg(Math.atan(v[2])),
|
||||
skew: (v) => (Math.abs(v[1]) + Math.abs(v[2])) / 2,
|
||||
};
|
||||
const rebaseAngle = (angle) => {
|
||||
angle = angle % 360;
|
||||
if (angle < 0)
|
||||
angle += 360;
|
||||
return angle;
|
||||
};
|
||||
const rotateZ = rotate;
|
||||
const scaleX = (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]);
|
||||
const scaleY = (v) => Math.sqrt(v[4] * v[4] + v[5] * v[5]);
|
||||
const matrix3dParsers = {
|
||||
x: 12,
|
||||
y: 13,
|
||||
z: 14,
|
||||
translateX: 12,
|
||||
translateY: 13,
|
||||
translateZ: 14,
|
||||
scaleX,
|
||||
scaleY,
|
||||
scale: (v) => (scaleX(v) + scaleY(v)) / 2,
|
||||
rotateX: (v) => rebaseAngle(radToDeg(Math.atan2(v[6], v[5]))),
|
||||
rotateY: (v) => rebaseAngle(radToDeg(Math.atan2(-v[2], v[0]))),
|
||||
rotateZ,
|
||||
rotate: rotateZ,
|
||||
skewX: (v) => radToDeg(Math.atan(v[4])),
|
||||
skewY: (v) => radToDeg(Math.atan(v[1])),
|
||||
skew: (v) => (Math.abs(v[1]) + Math.abs(v[4])) / 2,
|
||||
};
|
||||
function defaultTransformValue(name) {
|
||||
return name.includes("scale") ? 1 : 0;
|
||||
}
|
||||
function parseValueFromTransform(transform, name) {
|
||||
if (!transform || transform === "none") {
|
||||
return defaultTransformValue(name);
|
||||
}
|
||||
const matrix3dMatch = transform.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);
|
||||
let parsers;
|
||||
let match;
|
||||
if (matrix3dMatch) {
|
||||
parsers = matrix3dParsers;
|
||||
match = matrix3dMatch;
|
||||
}
|
||||
else {
|
||||
const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u);
|
||||
parsers = matrix2dParsers;
|
||||
match = matrix2dMatch;
|
||||
}
|
||||
if (!match) {
|
||||
return defaultTransformValue(name);
|
||||
}
|
||||
const valueParser = parsers[name];
|
||||
const values = match[1].split(",").map(convertTransformToNumber);
|
||||
return typeof valueParser === "function"
|
||||
? valueParser(values)
|
||||
: values[valueParser];
|
||||
}
|
||||
const readTransformValue = (instance, name) => {
|
||||
const { transform = "none" } = getComputedStyle(instance);
|
||||
return parseValueFromTransform(transform, name);
|
||||
};
|
||||
function convertTransformToNumber(value) {
|
||||
return parseFloat(value.trim());
|
||||
}
|
||||
|
||||
export { defaultTransformValue, parseValueFromTransform, readTransformValue };
|
||||
10
node_modules/motion-dom/dist/es/render/dom/style-computed.mjs
generated
vendored
Normal file
10
node_modules/motion-dom/dist/es/render/dom/style-computed.mjs
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import { isCSSVar } from './is-css-var.mjs';
|
||||
|
||||
function getComputedStyle(element, name) {
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
return isCSSVar(name)
|
||||
? computedStyle.getPropertyValue(name)
|
||||
: computedStyle[name];
|
||||
}
|
||||
|
||||
export { getComputedStyle };
|
||||
9
node_modules/motion-dom/dist/es/render/dom/style-set.mjs
generated
vendored
Normal file
9
node_modules/motion-dom/dist/es/render/dom/style-set.mjs
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { isCSSVar } from './is-css-var.mjs';
|
||||
|
||||
function setStyle(element, name, value) {
|
||||
isCSSVar(name)
|
||||
? element.style.setProperty(name, value)
|
||||
: (element.style[name] = value);
|
||||
}
|
||||
|
||||
export { setStyle };
|
||||
5
node_modules/motion-dom/dist/es/render/dom/utils/camel-to-dash.mjs
generated
vendored
Normal file
5
node_modules/motion-dom/dist/es/render/dom/utils/camel-to-dash.mjs
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
function camelToDash(str) {
|
||||
return str.replace(/([A-Z])/g, (match) => `-${match.toLowerCase()}`);
|
||||
}
|
||||
|
||||
export { camelToDash };
|
||||
13
node_modules/motion-dom/dist/es/render/utils/keys-position.mjs
generated
vendored
Normal file
13
node_modules/motion-dom/dist/es/render/utils/keys-position.mjs
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { transformPropOrder } from './keys-transform.mjs';
|
||||
|
||||
const positionalKeys = new Set([
|
||||
"width",
|
||||
"height",
|
||||
"top",
|
||||
"left",
|
||||
"right",
|
||||
"bottom",
|
||||
...transformPropOrder,
|
||||
]);
|
||||
|
||||
export { positionalKeys };
|
||||
28
node_modules/motion-dom/dist/es/render/utils/keys-transform.mjs
generated
vendored
Normal file
28
node_modules/motion-dom/dist/es/render/utils/keys-transform.mjs
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Generate a list of every possible transform key.
|
||||
*/
|
||||
const transformPropOrder = [
|
||||
"transformPerspective",
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
"translateX",
|
||||
"translateY",
|
||||
"translateZ",
|
||||
"scale",
|
||||
"scaleX",
|
||||
"scaleY",
|
||||
"rotate",
|
||||
"rotateX",
|
||||
"rotateY",
|
||||
"rotateZ",
|
||||
"skew",
|
||||
"skewX",
|
||||
"skewY",
|
||||
];
|
||||
/**
|
||||
* A quick lookup for transform props.
|
||||
*/
|
||||
const transformProps = /*@__PURE__*/ (() => new Set(transformPropOrder))();
|
||||
|
||||
export { transformPropOrder, transformProps };
|
||||
63
node_modules/motion-dom/dist/es/resize/handle-element.mjs
generated
vendored
Normal file
63
node_modules/motion-dom/dist/es/resize/handle-element.mjs
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
import { isSVGElement } from '../utils/is-svg-element.mjs';
|
||||
import { resolveElements } from '../utils/resolve-elements.mjs';
|
||||
|
||||
const resizeHandlers = new WeakMap();
|
||||
let observer;
|
||||
const getSize = (borderBoxAxis, svgAxis, htmlAxis) => (target, borderBoxSize) => {
|
||||
if (borderBoxSize && borderBoxSize[0]) {
|
||||
return borderBoxSize[0][(borderBoxAxis + "Size")];
|
||||
}
|
||||
else if (isSVGElement(target) && "getBBox" in target) {
|
||||
return target.getBBox()[svgAxis];
|
||||
}
|
||||
else {
|
||||
return target[htmlAxis];
|
||||
}
|
||||
};
|
||||
const getWidth = /*@__PURE__*/ getSize("inline", "width", "offsetWidth");
|
||||
const getHeight = /*@__PURE__*/ getSize("block", "height", "offsetHeight");
|
||||
function notifyTarget({ target, borderBoxSize }) {
|
||||
resizeHandlers.get(target)?.forEach((handler) => {
|
||||
handler(target, {
|
||||
get width() {
|
||||
return getWidth(target, borderBoxSize);
|
||||
},
|
||||
get height() {
|
||||
return getHeight(target, borderBoxSize);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
function notifyAll(entries) {
|
||||
entries.forEach(notifyTarget);
|
||||
}
|
||||
function createResizeObserver() {
|
||||
if (typeof ResizeObserver === "undefined")
|
||||
return;
|
||||
observer = new ResizeObserver(notifyAll);
|
||||
}
|
||||
function resizeElement(target, handler) {
|
||||
if (!observer)
|
||||
createResizeObserver();
|
||||
const elements = resolveElements(target);
|
||||
elements.forEach((element) => {
|
||||
let elementHandlers = resizeHandlers.get(element);
|
||||
if (!elementHandlers) {
|
||||
elementHandlers = new Set();
|
||||
resizeHandlers.set(element, elementHandlers);
|
||||
}
|
||||
elementHandlers.add(handler);
|
||||
observer?.observe(element);
|
||||
});
|
||||
return () => {
|
||||
elements.forEach((element) => {
|
||||
const elementHandlers = resizeHandlers.get(element);
|
||||
elementHandlers?.delete(handler);
|
||||
if (!elementHandlers?.size) {
|
||||
observer?.unobserve(element);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export { resizeElement };
|
||||
31
node_modules/motion-dom/dist/es/resize/handle-window.mjs
generated
vendored
Normal file
31
node_modules/motion-dom/dist/es/resize/handle-window.mjs
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
const windowCallbacks = new Set();
|
||||
let windowResizeHandler;
|
||||
function createWindowResizeHandler() {
|
||||
windowResizeHandler = () => {
|
||||
const info = {
|
||||
get width() {
|
||||
return window.innerWidth;
|
||||
},
|
||||
get height() {
|
||||
return window.innerHeight;
|
||||
},
|
||||
};
|
||||
windowCallbacks.forEach((callback) => callback(info));
|
||||
};
|
||||
window.addEventListener("resize", windowResizeHandler);
|
||||
}
|
||||
function resizeWindow(callback) {
|
||||
windowCallbacks.add(callback);
|
||||
if (!windowResizeHandler)
|
||||
createWindowResizeHandler();
|
||||
return () => {
|
||||
windowCallbacks.delete(callback);
|
||||
if (!windowCallbacks.size &&
|
||||
typeof windowResizeHandler === "function") {
|
||||
window.removeEventListener("resize", windowResizeHandler);
|
||||
windowResizeHandler = undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export { resizeWindow };
|
||||
8
node_modules/motion-dom/dist/es/resize/index.mjs
generated
vendored
Normal file
8
node_modules/motion-dom/dist/es/resize/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import { resizeElement } from './handle-element.mjs';
|
||||
import { resizeWindow } from './handle-window.mjs';
|
||||
|
||||
function resize(a, b) {
|
||||
return typeof a === "function" ? resizeWindow(a) : resizeElement(a, b);
|
||||
}
|
||||
|
||||
export { resize };
|
||||
18
node_modules/motion-dom/dist/es/scroll/observe.mjs
generated
vendored
Normal file
18
node_modules/motion-dom/dist/es/scroll/observe.mjs
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import { frame, cancelFrame } from '../frameloop/frame.mjs';
|
||||
|
||||
function observeTimeline(update, timeline) {
|
||||
let prevProgress;
|
||||
const onFrame = () => {
|
||||
const { currentTime } = timeline;
|
||||
const percentage = currentTime === null ? 0 : currentTime.value;
|
||||
const progress = percentage / 100;
|
||||
if (prevProgress !== progress) {
|
||||
update(progress);
|
||||
}
|
||||
prevProgress = progress;
|
||||
};
|
||||
frame.preUpdate(onFrame, true);
|
||||
return () => cancelFrame(onFrame);
|
||||
}
|
||||
|
||||
export { observeTimeline };
|
||||
7
node_modules/motion-dom/dist/es/stats/animation-count.mjs
generated
vendored
Normal file
7
node_modules/motion-dom/dist/es/stats/animation-count.mjs
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
const activeAnimations = {
|
||||
layout: 0,
|
||||
mainThread: 0,
|
||||
waapi: 0,
|
||||
};
|
||||
|
||||
export { activeAnimations };
|
||||
6
node_modules/motion-dom/dist/es/stats/buffer.mjs
generated
vendored
Normal file
6
node_modules/motion-dom/dist/es/stats/buffer.mjs
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
const statsBuffer = {
|
||||
value: null,
|
||||
addProjectionMetrics: null,
|
||||
};
|
||||
|
||||
export { statsBuffer };
|
||||
117
node_modules/motion-dom/dist/es/stats/index.mjs
generated
vendored
Normal file
117
node_modules/motion-dom/dist/es/stats/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
import { activeAnimations } from './animation-count.mjs';
|
||||
import { statsBuffer } from './buffer.mjs';
|
||||
import { frame, cancelFrame, frameData } from '../frameloop/frame.mjs';
|
||||
|
||||
function record() {
|
||||
const { value } = statsBuffer;
|
||||
if (value === null) {
|
||||
cancelFrame(record);
|
||||
return;
|
||||
}
|
||||
value.frameloop.rate.push(frameData.delta);
|
||||
value.animations.mainThread.push(activeAnimations.mainThread);
|
||||
value.animations.waapi.push(activeAnimations.waapi);
|
||||
value.animations.layout.push(activeAnimations.layout);
|
||||
}
|
||||
function mean(values) {
|
||||
return values.reduce((acc, value) => acc + value, 0) / values.length;
|
||||
}
|
||||
function summarise(values, calcAverage = mean) {
|
||||
if (values.length === 0) {
|
||||
return {
|
||||
min: 0,
|
||||
max: 0,
|
||||
avg: 0,
|
||||
};
|
||||
}
|
||||
return {
|
||||
min: Math.min(...values),
|
||||
max: Math.max(...values),
|
||||
avg: calcAverage(values),
|
||||
};
|
||||
}
|
||||
const msToFps = (ms) => Math.round(1000 / ms);
|
||||
function clearStatsBuffer() {
|
||||
statsBuffer.value = null;
|
||||
statsBuffer.addProjectionMetrics = null;
|
||||
}
|
||||
function reportStats() {
|
||||
const { value } = statsBuffer;
|
||||
if (!value) {
|
||||
throw new Error("Stats are not being measured");
|
||||
}
|
||||
clearStatsBuffer();
|
||||
cancelFrame(record);
|
||||
const summary = {
|
||||
frameloop: {
|
||||
setup: summarise(value.frameloop.setup),
|
||||
rate: summarise(value.frameloop.rate),
|
||||
read: summarise(value.frameloop.read),
|
||||
resolveKeyframes: summarise(value.frameloop.resolveKeyframes),
|
||||
preUpdate: summarise(value.frameloop.preUpdate),
|
||||
update: summarise(value.frameloop.update),
|
||||
preRender: summarise(value.frameloop.preRender),
|
||||
render: summarise(value.frameloop.render),
|
||||
postRender: summarise(value.frameloop.postRender),
|
||||
},
|
||||
animations: {
|
||||
mainThread: summarise(value.animations.mainThread),
|
||||
waapi: summarise(value.animations.waapi),
|
||||
layout: summarise(value.animations.layout),
|
||||
},
|
||||
layoutProjection: {
|
||||
nodes: summarise(value.layoutProjection.nodes),
|
||||
calculatedTargetDeltas: summarise(value.layoutProjection.calculatedTargetDeltas),
|
||||
calculatedProjections: summarise(value.layoutProjection.calculatedProjections),
|
||||
},
|
||||
};
|
||||
/**
|
||||
* Convert the rate to FPS
|
||||
*/
|
||||
const { rate } = summary.frameloop;
|
||||
rate.min = msToFps(rate.min);
|
||||
rate.max = msToFps(rate.max);
|
||||
rate.avg = msToFps(rate.avg);
|
||||
[rate.min, rate.max] = [rate.max, rate.min];
|
||||
return summary;
|
||||
}
|
||||
function recordStats() {
|
||||
if (statsBuffer.value) {
|
||||
clearStatsBuffer();
|
||||
throw new Error("Stats are already being measured");
|
||||
}
|
||||
const newStatsBuffer = statsBuffer;
|
||||
newStatsBuffer.value = {
|
||||
frameloop: {
|
||||
setup: [],
|
||||
rate: [],
|
||||
read: [],
|
||||
resolveKeyframes: [],
|
||||
preUpdate: [],
|
||||
update: [],
|
||||
preRender: [],
|
||||
render: [],
|
||||
postRender: [],
|
||||
},
|
||||
animations: {
|
||||
mainThread: [],
|
||||
waapi: [],
|
||||
layout: [],
|
||||
},
|
||||
layoutProjection: {
|
||||
nodes: [],
|
||||
calculatedTargetDeltas: [],
|
||||
calculatedProjections: [],
|
||||
},
|
||||
};
|
||||
newStatsBuffer.addProjectionMetrics = (metrics) => {
|
||||
const { layoutProjection } = newStatsBuffer.value;
|
||||
layoutProjection.nodes.push(metrics.nodes);
|
||||
layoutProjection.calculatedTargetDeltas.push(metrics.calculatedTargetDeltas);
|
||||
layoutProjection.calculatedProjections.push(metrics.calculatedProjections);
|
||||
};
|
||||
frame.postRender(record, true);
|
||||
return reportStats;
|
||||
}
|
||||
|
||||
export { recordStats };
|
||||
74
node_modules/motion-dom/dist/es/utils/interpolate.mjs
generated
vendored
Normal file
74
node_modules/motion-dom/dist/es/utils/interpolate.mjs
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
import { invariant, clamp, MotionGlobalConfig, noop, pipe, progress } from 'motion-utils';
|
||||
import { mix } from './mix/index.mjs';
|
||||
|
||||
function createMixers(output, ease, customMixer) {
|
||||
const mixers = [];
|
||||
const mixerFactory = customMixer || MotionGlobalConfig.mix || mix;
|
||||
const numMixers = output.length - 1;
|
||||
for (let i = 0; i < numMixers; i++) {
|
||||
let mixer = mixerFactory(output[i], output[i + 1]);
|
||||
if (ease) {
|
||||
const easingFunction = Array.isArray(ease) ? ease[i] || noop : ease;
|
||||
mixer = pipe(easingFunction, mixer);
|
||||
}
|
||||
mixers.push(mixer);
|
||||
}
|
||||
return mixers;
|
||||
}
|
||||
/**
|
||||
* Create a function that maps from a numerical input array to a generic output array.
|
||||
*
|
||||
* Accepts:
|
||||
* - Numbers
|
||||
* - Colors (hex, hsl, hsla, rgb, rgba)
|
||||
* - Complex (combinations of one or more numbers or strings)
|
||||
*
|
||||
* ```jsx
|
||||
* const mixColor = interpolate([0, 1], ['#fff', '#000'])
|
||||
*
|
||||
* mixColor(0.5) // 'rgba(128, 128, 128, 1)'
|
||||
* ```
|
||||
*
|
||||
* TODO Revisit this approach once we've moved to data models for values,
|
||||
* probably not needed to pregenerate mixer functions.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) {
|
||||
const inputLength = input.length;
|
||||
invariant(inputLength === output.length, "Both input and output ranges must be the same length", "range-length");
|
||||
/**
|
||||
* If we're only provided a single input, we can just make a function
|
||||
* that returns the output.
|
||||
*/
|
||||
if (inputLength === 1)
|
||||
return () => output[0];
|
||||
if (inputLength === 2 && output[0] === output[1])
|
||||
return () => output[1];
|
||||
const isZeroDeltaRange = input[0] === input[1];
|
||||
// If input runs highest -> lowest, reverse both arrays
|
||||
if (input[0] > input[inputLength - 1]) {
|
||||
input = [...input].reverse();
|
||||
output = [...output].reverse();
|
||||
}
|
||||
const mixers = createMixers(output, ease, mixer);
|
||||
const numMixers = mixers.length;
|
||||
const interpolator = (v) => {
|
||||
if (isZeroDeltaRange && v < input[0])
|
||||
return output[0];
|
||||
let i = 0;
|
||||
if (numMixers > 1) {
|
||||
for (; i < input.length - 2; i++) {
|
||||
if (v < input[i + 1])
|
||||
break;
|
||||
}
|
||||
}
|
||||
const progressInRange = progress(input[i], input[i + 1], v);
|
||||
return mixers[i](progressInRange);
|
||||
};
|
||||
return isClamp
|
||||
? (v) => interpolator(clamp(input[0], input[inputLength - 1], v))
|
||||
: interpolator;
|
||||
}
|
||||
|
||||
export { interpolate };
|
||||
11
node_modules/motion-dom/dist/es/utils/is-html-element.mjs
generated
vendored
Normal file
11
node_modules/motion-dom/dist/es/utils/is-html-element.mjs
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { isObject } from 'motion-utils';
|
||||
|
||||
/**
|
||||
* Checks if an element is an HTML element in a way
|
||||
* that works across iframes
|
||||
*/
|
||||
function isHTMLElement(element) {
|
||||
return isObject(element) && "offsetHeight" in element;
|
||||
}
|
||||
|
||||
export { isHTMLElement };
|
||||
11
node_modules/motion-dom/dist/es/utils/is-svg-element.mjs
generated
vendored
Normal file
11
node_modules/motion-dom/dist/es/utils/is-svg-element.mjs
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { isObject } from 'motion-utils';
|
||||
|
||||
/**
|
||||
* Checks if an element is an SVG element in a way
|
||||
* that works across iframes
|
||||
*/
|
||||
function isSVGElement(element) {
|
||||
return isObject(element) && "ownerSVGElement" in element;
|
||||
}
|
||||
|
||||
export { isSVGElement };
|
||||
11
node_modules/motion-dom/dist/es/utils/is-svg-svg-element.mjs
generated
vendored
Normal file
11
node_modules/motion-dom/dist/es/utils/is-svg-svg-element.mjs
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { isSVGElement } from './is-svg-element.mjs';
|
||||
|
||||
/**
|
||||
* Checks if an element is specifically an SVGSVGElement (the root SVG element)
|
||||
* in a way that works across iframes
|
||||
*/
|
||||
function isSVGSVGElement(element) {
|
||||
return isSVGElement(element) && element.tagName === "svg";
|
||||
}
|
||||
|
||||
export { isSVGSVGElement };
|
||||
47
node_modules/motion-dom/dist/es/utils/mix/color.mjs
generated
vendored
Normal file
47
node_modules/motion-dom/dist/es/utils/mix/color.mjs
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
import { warning } from 'motion-utils';
|
||||
import { hex } from '../../value/types/color/hex.mjs';
|
||||
import { hsla } from '../../value/types/color/hsla.mjs';
|
||||
import { hslaToRgba } from '../../value/types/color/hsla-to-rgba.mjs';
|
||||
import { rgba } from '../../value/types/color/rgba.mjs';
|
||||
import { mixImmediate } from './immediate.mjs';
|
||||
import { mixNumber } from './number.mjs';
|
||||
|
||||
// Linear color space blending
|
||||
// Explained https://www.youtube.com/watch?v=LKnqECcg6Gw
|
||||
// Demonstrated http://codepen.io/osublake/pen/xGVVaN
|
||||
const mixLinearColor = (from, to, v) => {
|
||||
const fromExpo = from * from;
|
||||
const expo = v * (to * to - fromExpo) + fromExpo;
|
||||
return expo < 0 ? 0 : Math.sqrt(expo);
|
||||
};
|
||||
const colorTypes = [hex, rgba, hsla];
|
||||
const getColorType = (v) => colorTypes.find((type) => type.test(v));
|
||||
function asRGBA(color) {
|
||||
const type = getColorType(color);
|
||||
warning(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`, "color-not-animatable");
|
||||
if (!Boolean(type))
|
||||
return false;
|
||||
let model = type.parse(color);
|
||||
if (type === hsla) {
|
||||
// TODO Remove this cast - needed since Motion's stricter typing
|
||||
model = hslaToRgba(model);
|
||||
}
|
||||
return model;
|
||||
}
|
||||
const mixColor = (from, to) => {
|
||||
const fromRGBA = asRGBA(from);
|
||||
const toRGBA = asRGBA(to);
|
||||
if (!fromRGBA || !toRGBA) {
|
||||
return mixImmediate(from, to);
|
||||
}
|
||||
const blended = { ...fromRGBA };
|
||||
return (v) => {
|
||||
blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v);
|
||||
blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v);
|
||||
blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v);
|
||||
blended.alpha = mixNumber(fromRGBA.alpha, toRGBA.alpha, v);
|
||||
return rgba.transform(blended);
|
||||
};
|
||||
};
|
||||
|
||||
export { mixColor, mixLinearColor };
|
||||
92
node_modules/motion-dom/dist/es/utils/mix/complex.mjs
generated
vendored
Normal file
92
node_modules/motion-dom/dist/es/utils/mix/complex.mjs
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
import { pipe, warning } from 'motion-utils';
|
||||
import { isCSSVariableToken } from '../../animation/utils/is-css-variable.mjs';
|
||||
import { color } from '../../value/types/color/index.mjs';
|
||||
import { complex, analyseComplexValue } from '../../value/types/complex/index.mjs';
|
||||
import { mixColor } from './color.mjs';
|
||||
import { mixImmediate } from './immediate.mjs';
|
||||
import { mixNumber as mixNumber$1 } from './number.mjs';
|
||||
import { invisibleValues, mixVisibility } from './visibility.mjs';
|
||||
|
||||
function mixNumber(a, b) {
|
||||
return (p) => mixNumber$1(a, b, p);
|
||||
}
|
||||
function getMixer(a) {
|
||||
if (typeof a === "number") {
|
||||
return mixNumber;
|
||||
}
|
||||
else if (typeof a === "string") {
|
||||
return isCSSVariableToken(a)
|
||||
? mixImmediate
|
||||
: color.test(a)
|
||||
? mixColor
|
||||
: mixComplex;
|
||||
}
|
||||
else if (Array.isArray(a)) {
|
||||
return mixArray;
|
||||
}
|
||||
else if (typeof a === "object") {
|
||||
return color.test(a) ? mixColor : mixObject;
|
||||
}
|
||||
return mixImmediate;
|
||||
}
|
||||
function mixArray(a, b) {
|
||||
const output = [...a];
|
||||
const numValues = output.length;
|
||||
const blendValue = a.map((v, i) => getMixer(v)(v, b[i]));
|
||||
return (p) => {
|
||||
for (let i = 0; i < numValues; i++) {
|
||||
output[i] = blendValue[i](p);
|
||||
}
|
||||
return output;
|
||||
};
|
||||
}
|
||||
function mixObject(a, b) {
|
||||
const output = { ...a, ...b };
|
||||
const blendValue = {};
|
||||
for (const key in output) {
|
||||
if (a[key] !== undefined && b[key] !== undefined) {
|
||||
blendValue[key] = getMixer(a[key])(a[key], b[key]);
|
||||
}
|
||||
}
|
||||
return (v) => {
|
||||
for (const key in blendValue) {
|
||||
output[key] = blendValue[key](v);
|
||||
}
|
||||
return output;
|
||||
};
|
||||
}
|
||||
function matchOrder(origin, target) {
|
||||
const orderedOrigin = [];
|
||||
const pointers = { color: 0, var: 0, number: 0 };
|
||||
for (let i = 0; i < target.values.length; i++) {
|
||||
const type = target.types[i];
|
||||
const originIndex = origin.indexes[type][pointers[type]];
|
||||
const originValue = origin.values[originIndex] ?? 0;
|
||||
orderedOrigin[i] = originValue;
|
||||
pointers[type]++;
|
||||
}
|
||||
return orderedOrigin;
|
||||
}
|
||||
const mixComplex = (origin, target) => {
|
||||
const template = complex.createTransformer(target);
|
||||
const originStats = analyseComplexValue(origin);
|
||||
const targetStats = analyseComplexValue(target);
|
||||
const canInterpolate = originStats.indexes.var.length === targetStats.indexes.var.length &&
|
||||
originStats.indexes.color.length === targetStats.indexes.color.length &&
|
||||
originStats.indexes.number.length >= targetStats.indexes.number.length;
|
||||
if (canInterpolate) {
|
||||
if ((invisibleValues.has(origin) &&
|
||||
!targetStats.values.length) ||
|
||||
(invisibleValues.has(target) &&
|
||||
!originStats.values.length)) {
|
||||
return mixVisibility(origin, target);
|
||||
}
|
||||
return pipe(mixArray(matchOrder(originStats, targetStats), targetStats.values), template);
|
||||
}
|
||||
else {
|
||||
warning(true, `Complex values '${origin}' and '${target}' too different to mix. Ensure all colors are of the same type, and that each contains the same quantity of number and color values. Falling back to instant transition.`, "complex-values-different");
|
||||
return mixImmediate(origin, target);
|
||||
}
|
||||
};
|
||||
|
||||
export { getMixer, mixArray, mixComplex, mixObject };
|
||||
5
node_modules/motion-dom/dist/es/utils/mix/immediate.mjs
generated
vendored
Normal file
5
node_modules/motion-dom/dist/es/utils/mix/immediate.mjs
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
function mixImmediate(a, b) {
|
||||
return (p) => (p > 0 ? b : a);
|
||||
}
|
||||
|
||||
export { mixImmediate };
|
||||
14
node_modules/motion-dom/dist/es/utils/mix/index.mjs
generated
vendored
Normal file
14
node_modules/motion-dom/dist/es/utils/mix/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import { getMixer } from './complex.mjs';
|
||||
import { mixNumber } from './number.mjs';
|
||||
|
||||
function mix(from, to, p) {
|
||||
if (typeof from === "number" &&
|
||||
typeof to === "number" &&
|
||||
typeof p === "number") {
|
||||
return mixNumber(from, to, p);
|
||||
}
|
||||
const mixer = getMixer(from);
|
||||
return mixer(from, to);
|
||||
}
|
||||
|
||||
export { mix };
|
||||
26
node_modules/motion-dom/dist/es/utils/mix/number.mjs
generated
vendored
Normal file
26
node_modules/motion-dom/dist/es/utils/mix/number.mjs
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Value in range from progress
|
||||
|
||||
Given a lower limit and an upper limit, we return the value within
|
||||
that range as expressed by progress (usually a number from 0 to 1)
|
||||
|
||||
So progress = 0.5 would change
|
||||
|
||||
from -------- to
|
||||
|
||||
to
|
||||
|
||||
from ---- to
|
||||
|
||||
E.g. from = 10, to = 20, progress = 0.5 => 15
|
||||
|
||||
@param [number]: Lower limit of range
|
||||
@param [number]: Upper limit of range
|
||||
@param [number]: The progress between lower and upper limits expressed 0-1
|
||||
@return [number]: Value as calculated from progress within range (not limited within range)
|
||||
*/
|
||||
const mixNumber = (from, to, progress) => {
|
||||
return from + (to - from) * progress;
|
||||
};
|
||||
|
||||
export { mixNumber };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user