feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,65 @@
'use strict';
import { defineAnimation, getReduceMotionForAnimation, recognizePrefixSuffix } from './util';
export const withClamp = function (config, _animationToClamp) {
'worklet';
return defineAnimation(_animationToClamp, () => {
'worklet';
const animationToClamp = typeof _animationToClamp === 'function' ? _animationToClamp() : _animationToClamp;
const strippedMin = config.min === undefined ? undefined : recognizePrefixSuffix(config.min).strippedValue;
const strippedMax = config.max === undefined ? undefined : recognizePrefixSuffix(config.max).strippedValue;
function clampOnFrame(animation, now) {
const finished = animationToClamp.onFrame(animationToClamp, now);
if (animationToClamp.current === undefined) {
console.warn("[Reanimated] Error inside 'withClamp' animation, the inner animation has invalid current value");
return true;
} else {
const {
prefix,
strippedValue,
suffix
} = recognizePrefixSuffix(animationToClamp.current);
let newValue;
if (strippedMax !== undefined && strippedMax < strippedValue) {
newValue = strippedMax;
} else if (strippedMin !== undefined && strippedMin > strippedValue) {
newValue = strippedMin;
} else {
newValue = strippedValue;
}
animation.current = typeof animationToClamp.current === 'number' ? newValue : `${prefix === undefined ? '' : prefix}${newValue}${suffix === undefined ? '' : suffix}`;
}
return finished;
}
function onStart(animation, value, now, previousAnimation) {
animation.current = value;
animation.previousAnimation = animationToClamp;
const animationBeforeClamped = previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.previousAnimation;
if (config.max !== undefined && config.min !== undefined && config.max < config.min) {
console.warn('[Reanimated] Wrong config was provided to withClamp. Min value is bigger than max');
}
animationToClamp.onStart(animationToClamp,
/** provide the current value of the previous animation of the clamped animation
so we can animate from the original "un-truncated" value
*/
(animationBeforeClamped === null || animationBeforeClamped === void 0 ? void 0 : animationBeforeClamped.current) || value, now, animationBeforeClamped);
}
const callback = finished => {
if (animationToClamp.callback) {
animationToClamp.callback(finished);
}
};
return {
isHigherOrder: true,
onFrame: clampOnFrame,
onStart,
current: animationToClamp.current,
callback,
previousAnimation: null,
reduceMotion: getReduceMotionForAnimation(config.reduceMotion)
};
});
};
//# sourceMappingURL=clamp.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
'use strict';
export {};
//# sourceMappingURL=commonTypes.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":[],"sources":["commonTypes.ts"],"sourcesContent":["'use strict';\nimport type {\n StyleProps,\n AnimatableValue,\n AnimationObject,\n Animation,\n Timestamp,\n AnimationCallback,\n} from '../commonTypes';\nimport type { AnimatedStyle } from '../helperTypes';\n\nexport interface HigherOrderAnimation {\n isHigherOrder?: boolean;\n}\n\nexport type NextAnimation<T extends AnimationObject> = T | (() => T);\n\nexport interface ClampAnimation\n extends Animation<ClampAnimation>,\n HigherOrderAnimation {\n current: AnimatableValue;\n}\n\nexport interface DelayAnimation\n extends Animation<DelayAnimation>,\n HigherOrderAnimation {\n startTime: Timestamp;\n started: boolean;\n previousAnimation: DelayAnimation | null;\n current: AnimatableValue;\n}\n\nexport interface RepeatAnimation\n extends Animation<RepeatAnimation>,\n HigherOrderAnimation {\n reps: number;\n startValue: AnimatableValue;\n toValue?: AnimatableValue;\n previousAnimation?: RepeatAnimation;\n}\n\nexport interface SequenceAnimation\n extends Animation<SequenceAnimation>,\n HigherOrderAnimation {\n animationIndex: number;\n}\n\nexport interface StyleLayoutAnimation extends HigherOrderAnimation {\n current: StyleProps;\n styleAnimations: AnimatedStyle<any>;\n onFrame: (animation: StyleLayoutAnimation, timestamp: Timestamp) => boolean;\n onStart: (\n nextAnimation: StyleLayoutAnimation,\n current: AnimatedStyle<any>,\n timestamp: Timestamp,\n previousAnimation: StyleLayoutAnimation\n ) => void;\n callback?: AnimationCallback;\n}\n"],"mappings":"AAAA,YAAY;;AAAC","ignoreList":[]}

View File

@@ -0,0 +1,80 @@
'use strict';
import { defineAnimation, getReduceMotionForAnimation } from '../util';
import { rubberBandDecay } from './rubberBandDecay';
import { isValidRubberBandConfig } from './utils';
import { rigidDecay } from './rigidDecay';
// TODO TYPESCRIPT This is a temporary type to get rid of .d.ts file.
function validateConfig(config) {
'worklet';
if (config.clamp) {
if (!Array.isArray(config.clamp)) {
throw new Error(`[Reanimated] \`config.clamp\` must be an array but is ${typeof config.clamp}.`);
}
if (config.clamp.length !== 2) {
throw new Error(`[Reanimated] \`clamp array\` must contain 2 items but is given ${config.clamp.length}.`);
}
}
if (config.velocityFactor <= 0) {
throw new Error(`[Reanimated] \`config.velocityFactor\` must be greather then 0 but is ${config.velocityFactor}.`);
}
if (config.rubberBandEffect && !config.clamp) {
throw new Error('[Reanimated] You need to set `clamp` property when using `rubberBandEffect`.');
}
}
/**
* Lets you create animations that mimic objects in motion with friction.
*
* @param config - The decay animation configuration - {@link DecayConfig}.
* @param callback - A function called upon animation completion - {@link AnimationCallback}.
* @returns An [animation object](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary#animation-object) which holds the current state of the animation.
* @see https://docs.swmansion.com/react-native-reanimated/docs/animations/withDecay
*/
export const withDecay = function (userConfig, callback) {
'worklet';
return defineAnimation(0, () => {
'worklet';
const config = {
deceleration: 0.998,
velocityFactor: 1,
velocity: 0,
rubberBandFactor: 0.6
};
if (userConfig) {
Object.keys(userConfig).forEach(key => config[key] = userConfig[key]);
}
const decay = isValidRubberBandConfig(config) ? (animation, now) => rubberBandDecay(animation, now, config) : (animation, now) => rigidDecay(animation, now, config);
function onStart(animation, value, now) {
animation.current = value;
animation.lastTimestamp = now;
animation.startTimestamp = now;
animation.initialVelocity = config.velocity;
validateConfig(config);
if (animation.reduceMotion && config.clamp) {
if (value < config.clamp[0]) {
animation.current = config.clamp[0];
} else if (value > config.clamp[1]) {
animation.current = config.clamp[1];
}
}
}
return {
onFrame: decay,
onStart,
callback,
velocity: config.velocity ?? 0,
initialVelocity: 0,
current: 0,
lastTimestamp: 0,
startTimestamp: 0,
reduceMotion: getReduceMotionForAnimation(config.reduceMotion)
};
});
};
//# sourceMappingURL=decay.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
'use strict';
export { withDecay } from './decay';
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["withDecay"],"sources":["index.ts"],"sourcesContent":["'use strict';\nexport type { WithDecayConfig } from './decay';\nexport { withDecay } from './decay';\nexport type { DecayAnimation } from './utils';\n"],"mappings":"AAAA,YAAY;;AAEZ,SAASA,SAAS,QAAQ,SAAS","ignoreList":[]}

View File

@@ -0,0 +1,30 @@
'use strict';
import { SLOPE_FACTOR, VELOCITY_EPS } from './utils';
export function rigidDecay(animation, now, config) {
'worklet';
const {
lastTimestamp,
startTimestamp,
initialVelocity,
current,
velocity
} = animation;
const deltaTime = Math.min(now - lastTimestamp, 64);
const v = velocity * Math.exp(-(1 - config.deceleration) * (now - startTimestamp) * SLOPE_FACTOR);
animation.current = current + v * config.velocityFactor * deltaTime / 1000;
animation.velocity = v;
animation.lastTimestamp = now;
if (config.clamp) {
if (initialVelocity < 0 && animation.current <= config.clamp[0]) {
animation.current = config.clamp[0];
return true;
} else if (initialVelocity > 0 && animation.current >= config.clamp[1]) {
animation.current = config.clamp[1];
return true;
}
}
return Math.abs(v) < VELOCITY_EPS;
}
//# sourceMappingURL=rigidDecay.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["SLOPE_FACTOR","VELOCITY_EPS","rigidDecay","animation","now","config","lastTimestamp","startTimestamp","initialVelocity","current","velocity","deltaTime","Math","min","v","exp","deceleration","velocityFactor","clamp","abs"],"sources":["rigidDecay.ts"],"sourcesContent":["'use strict';\nimport { SLOPE_FACTOR, VELOCITY_EPS } from './utils';\nimport type { DefaultDecayConfig, InnerDecayAnimation } from './utils';\n\nexport function rigidDecay(\n animation: InnerDecayAnimation,\n now: number,\n config: DefaultDecayConfig\n): boolean {\n 'worklet';\n const { lastTimestamp, startTimestamp, initialVelocity, current, velocity } =\n animation;\n\n const deltaTime = Math.min(now - lastTimestamp, 64);\n const v =\n velocity *\n Math.exp(\n -(1 - config.deceleration) * (now - startTimestamp) * SLOPE_FACTOR\n );\n animation.current = current + (v * config.velocityFactor * deltaTime) / 1000;\n animation.velocity = v;\n animation.lastTimestamp = now;\n\n if (config.clamp) {\n if (initialVelocity < 0 && animation.current <= config.clamp[0]) {\n animation.current = config.clamp[0];\n return true;\n } else if (initialVelocity > 0 && animation.current >= config.clamp[1]) {\n animation.current = config.clamp[1];\n return true;\n }\n }\n return Math.abs(v) < VELOCITY_EPS;\n}\n"],"mappings":"AAAA,YAAY;;AACZ,SAASA,YAAY,EAAEC,YAAY,QAAQ,SAAS;AAGpD,OAAO,SAASC,UAAUA,CACxBC,SAA8B,EAC9BC,GAAW,EACXC,MAA0B,EACjB;EACT,SAAS;;EACT,MAAM;IAAEC,aAAa;IAAEC,cAAc;IAAEC,eAAe;IAAEC,OAAO;IAAEC;EAAS,CAAC,GACzEP,SAAS;EAEX,MAAMQ,SAAS,GAAGC,IAAI,CAACC,GAAG,CAACT,GAAG,GAAGE,aAAa,EAAE,EAAE,CAAC;EACnD,MAAMQ,CAAC,GACLJ,QAAQ,GACRE,IAAI,CAACG,GAAG,CACN,EAAE,CAAC,GAAGV,MAAM,CAACW,YAAY,CAAC,IAAIZ,GAAG,GAAGG,cAAc,CAAC,GAAGP,YACxD,CAAC;EACHG,SAAS,CAACM,OAAO,GAAGA,OAAO,GAAIK,CAAC,GAAGT,MAAM,CAACY,cAAc,GAAGN,SAAS,GAAI,IAAI;EAC5ER,SAAS,CAACO,QAAQ,GAAGI,CAAC;EACtBX,SAAS,CAACG,aAAa,GAAGF,GAAG;EAE7B,IAAIC,MAAM,CAACa,KAAK,EAAE;IAChB,IAAIV,eAAe,GAAG,CAAC,IAAIL,SAAS,CAACM,OAAO,IAAIJ,MAAM,CAACa,KAAK,CAAC,CAAC,CAAC,EAAE;MAC/Df,SAAS,CAACM,OAAO,GAAGJ,MAAM,CAACa,KAAK,CAAC,CAAC,CAAC;MACnC,OAAO,IAAI;IACb,CAAC,MAAM,IAAIV,eAAe,GAAG,CAAC,IAAIL,SAAS,CAACM,OAAO,IAAIJ,MAAM,CAACa,KAAK,CAAC,CAAC,CAAC,EAAE;MACtEf,SAAS,CAACM,OAAO,GAAGJ,MAAM,CAACa,KAAK,CAAC,CAAC,CAAC;MACnC,OAAO,IAAI;IACb;EACF;EACA,OAAON,IAAI,CAACO,GAAG,CAACL,CAAC,CAAC,GAAGb,YAAY;AACnC","ignoreList":[]}

View File

@@ -0,0 +1,34 @@
'use strict';
import { SLOPE_FACTOR, VELOCITY_EPS } from './utils';
const DERIVATIVE_EPS = 0.1;
export function rubberBandDecay(animation, now, config) {
'worklet';
const {
lastTimestamp,
startTimestamp,
current,
velocity
} = animation;
const deltaTime = Math.min(now - lastTimestamp, 64);
const clampIndex = Math.abs(current - config.clamp[0]) < Math.abs(current - config.clamp[1]) ? 0 : 1;
let derivative = 0;
if (current < config.clamp[0] || current > config.clamp[1]) {
derivative = current - config.clamp[clampIndex];
}
const v = velocity * Math.exp(-(1 - config.deceleration) * (now - startTimestamp) * SLOPE_FACTOR) - derivative * config.rubberBandFactor;
if (Math.abs(derivative) > DERIVATIVE_EPS) {
animation.springActive = true;
} else if (animation.springActive) {
animation.current = config.clamp[clampIndex];
return true;
} else if (Math.abs(v) < VELOCITY_EPS) {
return true;
}
animation.current = current + v * config.velocityFactor * deltaTime / 1000;
animation.velocity = v;
animation.lastTimestamp = now;
return false;
}
//# sourceMappingURL=rubberBandDecay.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["SLOPE_FACTOR","VELOCITY_EPS","DERIVATIVE_EPS","rubberBandDecay","animation","now","config","lastTimestamp","startTimestamp","current","velocity","deltaTime","Math","min","clampIndex","abs","clamp","derivative","v","exp","deceleration","rubberBandFactor","springActive","velocityFactor"],"sources":["rubberBandDecay.ts"],"sourcesContent":["'use strict';\nimport type { RubberBandDecayConfig, InnerDecayAnimation } from './utils';\nimport { SLOPE_FACTOR, VELOCITY_EPS } from './utils';\n\nconst DERIVATIVE_EPS = 0.1;\n\nexport function rubberBandDecay(\n animation: InnerDecayAnimation,\n now: number,\n config: RubberBandDecayConfig\n): boolean {\n 'worklet';\n const { lastTimestamp, startTimestamp, current, velocity } = animation;\n\n const deltaTime = Math.min(now - lastTimestamp, 64);\n const clampIndex =\n Math.abs(current - config.clamp[0]) < Math.abs(current - config.clamp[1])\n ? 0\n : 1;\n\n let derivative = 0;\n if (current < config.clamp[0] || current > config.clamp[1]) {\n derivative = current - config.clamp[clampIndex];\n }\n\n const v =\n velocity *\n Math.exp(\n -(1 - config.deceleration) * (now - startTimestamp) * SLOPE_FACTOR\n ) -\n derivative * config.rubberBandFactor;\n\n if (Math.abs(derivative) > DERIVATIVE_EPS) {\n animation.springActive = true;\n } else if (animation.springActive) {\n animation.current = config.clamp[clampIndex];\n return true;\n } else if (Math.abs(v) < VELOCITY_EPS) {\n return true;\n }\n\n animation.current = current + (v * config.velocityFactor * deltaTime) / 1000;\n animation.velocity = v;\n animation.lastTimestamp = now;\n return false;\n}\n"],"mappings":"AAAA,YAAY;;AAEZ,SAASA,YAAY,EAAEC,YAAY,QAAQ,SAAS;AAEpD,MAAMC,cAAc,GAAG,GAAG;AAE1B,OAAO,SAASC,eAAeA,CAC7BC,SAA8B,EAC9BC,GAAW,EACXC,MAA6B,EACpB;EACT,SAAS;;EACT,MAAM;IAAEC,aAAa;IAAEC,cAAc;IAAEC,OAAO;IAAEC;EAAS,CAAC,GAAGN,SAAS;EAEtE,MAAMO,SAAS,GAAGC,IAAI,CAACC,GAAG,CAACR,GAAG,GAAGE,aAAa,EAAE,EAAE,CAAC;EACnD,MAAMO,UAAU,GACdF,IAAI,CAACG,GAAG,CAACN,OAAO,GAAGH,MAAM,CAACU,KAAK,CAAC,CAAC,CAAC,CAAC,GAAGJ,IAAI,CAACG,GAAG,CAACN,OAAO,GAAGH,MAAM,CAACU,KAAK,CAAC,CAAC,CAAC,CAAC,GACrE,CAAC,GACD,CAAC;EAEP,IAAIC,UAAU,GAAG,CAAC;EAClB,IAAIR,OAAO,GAAGH,MAAM,CAACU,KAAK,CAAC,CAAC,CAAC,IAAIP,OAAO,GAAGH,MAAM,CAACU,KAAK,CAAC,CAAC,CAAC,EAAE;IAC1DC,UAAU,GAAGR,OAAO,GAAGH,MAAM,CAACU,KAAK,CAACF,UAAU,CAAC;EACjD;EAEA,MAAMI,CAAC,GACLR,QAAQ,GACNE,IAAI,CAACO,GAAG,CACN,EAAE,CAAC,GAAGb,MAAM,CAACc,YAAY,CAAC,IAAIf,GAAG,GAAGG,cAAc,CAAC,GAAGR,YACxD,CAAC,GACHiB,UAAU,GAAGX,MAAM,CAACe,gBAAgB;EAEtC,IAAIT,IAAI,CAACG,GAAG,CAACE,UAAU,CAAC,GAAGf,cAAc,EAAE;IACzCE,SAAS,CAACkB,YAAY,GAAG,IAAI;EAC/B,CAAC,MAAM,IAAIlB,SAAS,CAACkB,YAAY,EAAE;IACjClB,SAAS,CAACK,OAAO,GAAGH,MAAM,CAACU,KAAK,CAACF,UAAU,CAAC;IAC5C,OAAO,IAAI;EACb,CAAC,MAAM,IAAIF,IAAI,CAACG,GAAG,CAACG,CAAC,CAAC,GAAGjB,YAAY,EAAE;IACrC,OAAO,IAAI;EACb;EAEAG,SAAS,CAACK,OAAO,GAAGA,OAAO,GAAIS,CAAC,GAAGZ,MAAM,CAACiB,cAAc,GAAGZ,SAAS,GAAI,IAAI;EAC5EP,SAAS,CAACM,QAAQ,GAAGQ,CAAC;EACtBd,SAAS,CAACG,aAAa,GAAGF,GAAG;EAC7B,OAAO,KAAK;AACd","ignoreList":[]}

View File

@@ -0,0 +1,28 @@
'use strict';
import { isWeb } from '../../PlatformChecker';
const IS_WEB = isWeb();
export const VELOCITY_EPS = IS_WEB ? 1 / 20 : 1;
export const SLOPE_FACTOR = 0.1;
/**
* The decay animation configuration.
*
* @param velocity - Initial velocity of the animation. Defaults to 0.
* @param deceleration - The rate at which the velocity decreases over time. Defaults to 0.998.
* @param clamp - Array of two numbers which restricts animation's range. Defaults to [].
* @param velocityFactor - Velocity multiplier. Defaults to 1.
* @param rubberBandEffect - Makes the animation bounce over the limit specified in `clamp`. Defaults to `false`.
* @param rubberBandFactor - Strength of the rubber band effect. Defaults to 0.6.
* @param reduceMotion - Determines how the animation responds to the device's reduced motion accessibility setting. Default to `ReduceMotion.System` - {@link ReduceMotion}.
* @see https://docs.swmansion.com/react-native-reanimated/docs/animations/withDecay#config
*/
// If user wants to use rubber band decay animation we have to make sure he has provided clamp
export function isValidRubberBandConfig(config) {
'worklet';
return !!config.rubberBandEffect && Array.isArray(config.clamp) && config.clamp.length === 2;
}
//# sourceMappingURL=utils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["isWeb","IS_WEB","VELOCITY_EPS","SLOPE_FACTOR","isValidRubberBandConfig","config","rubberBandEffect","Array","isArray","clamp","length"],"sources":["utils.ts"],"sourcesContent":["'use strict';\nimport type {\n AnimatableValue,\n AnimationObject,\n Animation,\n ReduceMotion,\n Timestamp,\n RequiredKeys,\n} from '../../../reanimated2/commonTypes';\nimport { isWeb } from '../../PlatformChecker';\n\nconst IS_WEB = isWeb();\nexport const VELOCITY_EPS = IS_WEB ? 1 / 20 : 1;\nexport const SLOPE_FACTOR = 0.1;\n\nexport interface DecayAnimation extends Animation<DecayAnimation> {\n lastTimestamp: Timestamp;\n startTimestamp: Timestamp;\n initialVelocity: number;\n velocity: number;\n current: AnimatableValue;\n}\n\nexport interface InnerDecayAnimation\n extends Omit<DecayAnimation, 'current'>,\n AnimationObject {\n current: number;\n springActive?: boolean;\n}\n\n/**\n * The decay animation configuration.\n *\n * @param velocity - Initial velocity of the animation. Defaults to 0.\n * @param deceleration - The rate at which the velocity decreases over time. Defaults to 0.998.\n * @param clamp - Array of two numbers which restricts animation's range. Defaults to [].\n * @param velocityFactor - Velocity multiplier. Defaults to 1.\n * @param rubberBandEffect - Makes the animation bounce over the limit specified in `clamp`. Defaults to `false`.\n * @param rubberBandFactor - Strength of the rubber band effect. Defaults to 0.6.\n * @param reduceMotion - Determines how the animation responds to the device's reduced motion accessibility setting. Default to `ReduceMotion.System` - {@link ReduceMotion}.\n * @see https://docs.swmansion.com/react-native-reanimated/docs/animations/withDecay#config\n */\nexport type DecayConfig = {\n deceleration?: number;\n velocityFactor?: number;\n velocity?: number;\n reduceMotion?: ReduceMotion;\n} & (\n | {\n rubberBandEffect?: false;\n clamp?: [min: number, max: number];\n }\n | {\n rubberBandEffect: true;\n clamp: [min: number, max: number];\n rubberBandFactor?: number;\n }\n);\n\nexport type DefaultDecayConfig = RequiredKeys<\n DecayConfig,\n 'deceleration' | 'velocityFactor' | 'velocity'\n> & { rubberBandFactor: number };\n\n// If user wants to use rubber band decay animation we have to make sure he has provided clamp\nexport type RubberBandDecayConfig = RequiredKeys<\n DefaultDecayConfig,\n 'clamp'\n> & { rubberBandEffect: true };\n\nexport function isValidRubberBandConfig(\n config: DefaultDecayConfig\n): config is RubberBandDecayConfig {\n 'worklet';\n return (\n !!config.rubberBandEffect &&\n Array.isArray(config.clamp) &&\n config.clamp.length === 2\n );\n}\n"],"mappings":"AAAA,YAAY;;AASZ,SAASA,KAAK,QAAQ,uBAAuB;AAE7C,MAAMC,MAAM,GAAGD,KAAK,CAAC,CAAC;AACtB,OAAO,MAAME,YAAY,GAAGD,MAAM,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC;AAC/C,OAAO,MAAME,YAAY,GAAG,GAAG;;AAiB/B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAuBA;;AAMA,OAAO,SAASC,uBAAuBA,CACrCC,MAA0B,EACO;EACjC,SAAS;;EACT,OACE,CAAC,CAACA,MAAM,CAACC,gBAAgB,IACzBC,KAAK,CAACC,OAAO,CAACH,MAAM,CAACI,KAAK,CAAC,IAC3BJ,MAAM,CAACI,KAAK,CAACC,MAAM,KAAK,CAAC;AAE7B","ignoreList":[]}

View File

@@ -0,0 +1,82 @@
'use strict';
import { defineAnimation, getReduceMotionForAnimation } from './util';
// TODO TYPESCRIPT This is a temporary type to get rid of .d.ts file.
/**
* An animation modifier that lets you start an animation with a delay.
*
* @param delayMs - Duration (in milliseconds) before the animation starts.
* @param nextAnimation - The animation to delay.
* @param reduceMotion - Determines how the animation responds to the device's reduced motion accessibility setting. Default to `ReduceMotion.System` - {@link ReduceMotion}.
* @returns An [animation object](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary#animation-object) which holds the current state of the animation.
* @see https://docs.swmansion.com/react-native-reanimated/docs/animations/withDelay
*/
export const withDelay = function (delayMs, _nextAnimation, reduceMotion) {
'worklet';
return defineAnimation(_nextAnimation, () => {
'worklet';
const nextAnimation = typeof _nextAnimation === 'function' ? _nextAnimation() : _nextAnimation;
function delay(animation, now) {
const {
startTime,
started,
previousAnimation
} = animation;
const current = animation.current;
if (now - startTime > delayMs || animation.reduceMotion) {
if (!started) {
nextAnimation.onStart(nextAnimation, current, now, previousAnimation);
animation.previousAnimation = null;
animation.started = true;
}
const finished = nextAnimation.onFrame(nextAnimation, now);
animation.current = nextAnimation.current;
return finished;
} else if (previousAnimation) {
const finished = previousAnimation.finished || previousAnimation.onFrame(previousAnimation, now);
animation.current = previousAnimation.current;
if (finished) {
animation.previousAnimation = null;
}
}
return false;
}
function onStart(animation, value, now, previousAnimation) {
animation.startTime = now;
animation.started = false;
animation.current = value;
if (previousAnimation === animation) {
animation.previousAnimation = previousAnimation.previousAnimation;
} else {
animation.previousAnimation = previousAnimation;
}
// child animations inherit the setting, unless they already have it defined
// they will have it defined only if the user used the `reduceMotion` prop
if (nextAnimation.reduceMotion === undefined) {
nextAnimation.reduceMotion = animation.reduceMotion;
}
}
const callback = finished => {
if (nextAnimation.callback) {
nextAnimation.callback(finished);
}
};
return {
isHigherOrder: true,
onFrame: delay,
onStart,
current: nextAnimation.current,
callback,
previousAnimation: null,
startTime: 0,
started: false,
reduceMotion: getReduceMotionForAnimation(reduceMotion)
};
});
};
//# sourceMappingURL=delay.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,12 @@
'use strict';
export { cancelAnimation, defineAnimation, initialUpdaterRun } from './util';
export { withTiming } from './timing';
export { withSpring } from './spring';
export { withDecay } from './decay';
export { withClamp } from './clamp';
export { withDelay } from './delay';
export { withRepeat } from './repeat';
export { withSequence } from './sequence';
export { withStyleAnimation } from './styleAnimation';
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["cancelAnimation","defineAnimation","initialUpdaterRun","withTiming","withSpring","withDecay","withClamp","withDelay","withRepeat","withSequence","withStyleAnimation"],"sources":["index.ts"],"sourcesContent":["'use strict';\nexport type {\n HigherOrderAnimation,\n NextAnimation,\n DelayAnimation,\n RepeatAnimation,\n SequenceAnimation,\n StyleLayoutAnimation,\n} from './commonTypes';\nexport { cancelAnimation, defineAnimation, initialUpdaterRun } from './util';\nexport { withTiming } from './timing';\nexport type { TimingAnimation, WithTimingConfig } from './timing';\nexport { withSpring } from './spring';\nexport type { SpringAnimation, WithSpringConfig } from './springUtils';\nexport { withDecay } from './decay';\nexport type { DecayAnimation, WithDecayConfig } from './decay';\nexport { withClamp } from './clamp';\nexport { withDelay } from './delay';\nexport { withRepeat } from './repeat';\nexport { withSequence } from './sequence';\nexport { withStyleAnimation } from './styleAnimation';\n"],"mappings":"AAAA,YAAY;;AASZ,SAASA,eAAe,EAAEC,eAAe,EAAEC,iBAAiB,QAAQ,QAAQ;AAC5E,SAASC,UAAU,QAAQ,UAAU;AAErC,SAASC,UAAU,QAAQ,UAAU;AAErC,SAASC,SAAS,QAAQ,SAAS;AAEnC,SAASC,SAAS,QAAQ,SAAS;AACnC,SAASC,SAAS,QAAQ,SAAS;AACnC,SAASC,UAAU,QAAQ,UAAU;AACrC,SAASC,YAAY,QAAQ,YAAY;AACzC,SAASC,kBAAkB,QAAQ,kBAAkB","ignoreList":[]}

View File

@@ -0,0 +1,88 @@
'use strict';
import { defineAnimation, getReduceMotionForAnimation } from './util';
// TODO TYPESCRIPT This is a temporary type to get rid of .d.ts file.
/**
* Lets you repeat an animation given number of times or run it indefinitely.
*
* @param animation - An animation object you want to repeat.
* @param numberOfReps - The number of times the animation is going to be repeated. Defaults to 2.
* @param reverse - Whether the animation should run in reverse every other repetition. Defaults to false.
* @param callback - A function called on animation complete.
* @param reduceMotion - Determines how the animation responds to the device's reduced motion accessibility setting. Default to `ReduceMotion.System` - {@link ReduceMotion}.
* @returns An [animation object](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary#animation-object) which holds the current state of the animation.
* @see https://docs.swmansion.com/react-native-reanimated/docs/animations/withRepeat
*/
export const withRepeat = function (_nextAnimation, numberOfReps = 2, reverse = false, callback, reduceMotion) {
'worklet';
return defineAnimation(_nextAnimation, () => {
'worklet';
const nextAnimation = typeof _nextAnimation === 'function' ? _nextAnimation() : _nextAnimation;
function repeat(animation, now) {
const finished = nextAnimation.onFrame(nextAnimation, now);
animation.current = nextAnimation.current;
if (finished) {
animation.reps += 1;
// call inner animation's callback on every repetition
// as the second argument the animation's current value is passed
if (nextAnimation.callback) {
nextAnimation.callback(true /* finished */, animation.current);
}
if (animation.reduceMotion || numberOfReps > 0 && animation.reps >= numberOfReps) {
return true;
}
const startValue = reverse ? nextAnimation.current : animation.startValue;
if (reverse) {
nextAnimation.toValue = animation.startValue;
animation.startValue = startValue;
}
nextAnimation.onStart(nextAnimation, startValue, now, nextAnimation.previousAnimation);
return false;
}
return false;
}
const repCallback = finished => {
if (callback) {
callback(finished);
}
// when cancelled call inner animation's callback
if (!finished && nextAnimation.callback) {
nextAnimation.callback(false /* finished */);
}
};
function onStart(animation, value, now, previousAnimation) {
animation.startValue = value;
animation.reps = 0;
// child animations inherit the setting, unless they already have it defined
// they will have it defined only if the user used the `reduceMotion` prop
if (nextAnimation.reduceMotion === undefined) {
nextAnimation.reduceMotion = animation.reduceMotion;
}
// don't start the animation if reduced motion is enabled and
// the animation would end at its starting point
if (animation.reduceMotion && reverse && (numberOfReps <= 0 || numberOfReps % 2 === 0)) {
animation.current = animation.startValue;
animation.onFrame = () => true;
} else {
nextAnimation.onStart(nextAnimation, value, now, previousAnimation);
}
}
return {
isHigherOrder: true,
onFrame: repeat,
onStart,
reps: 0,
current: nextAnimation.current,
callback: repCallback,
startValue: 0,
reduceMotion: getReduceMotionForAnimation(reduceMotion)
};
});
};
//# sourceMappingURL=repeat.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,117 @@
'use strict';
import { defineAnimation, getReduceMotionForAnimation } from './util';
/**
* Lets you run animations in a sequence.
*
* @param reduceMotion - Determines how the animation responds to the device's reduced motion accessibility setting. Default to `ReduceMotion.System` - {@link ReduceMotion}.
* @param animations - Any number of animation objects to be run in a sequence.
* @returns An [animation object](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary#animation-object) which holds the current state of the animation/
* @see https://docs.swmansion.com/react-native-reanimated/docs/animations/withSequence
*/
export function withSequence(_reduceMotionOrFirstAnimation, ..._animations) {
'worklet';
let reduceMotion;
// the first argument is either a config or an animation
// this is done to allow the reduce motion config prop to be optional
if (_reduceMotionOrFirstAnimation) {
if (typeof _reduceMotionOrFirstAnimation === 'string') {
reduceMotion = _reduceMotionOrFirstAnimation;
} else {
_animations.unshift(_reduceMotionOrFirstAnimation);
}
}
if (_animations.length === 0) {
console.warn('[Reanimated] No animation was provided for the sequence');
return defineAnimation(0, () => {
'worklet';
return {
onStart: (animation, value) => animation.current = value,
onFrame: () => true,
current: 0,
animationIndex: 0,
reduceMotion: getReduceMotionForAnimation(reduceMotion)
};
});
}
return defineAnimation(_animations[0], () => {
'worklet';
const animations = _animations.map(a => {
const result = typeof a === 'function' ? a() : a;
result.finished = false;
return result;
});
function findNextNonReducedMotionAnimationIndex(index) {
// the last animation is returned even if reduced motion is enabled,
// because we want the sequence to finish at the right spot
while (index < animations.length - 1 && animations[index].reduceMotion) {
index++;
}
return index;
}
const callback = finished => {
if (finished) {
// we want to call the callback after every single animation
// not after all of them
return;
}
// this is going to be called only if sequence has been cancelled
animations.forEach(animation => {
if (typeof animation.callback === 'function' && !animation.finished) {
animation.callback(finished);
}
});
};
function sequence(animation, now) {
const currentAnim = animations[animation.animationIndex];
const finished = currentAnim.onFrame(currentAnim, now);
animation.current = currentAnim.current;
if (finished) {
// we want to call the callback after every single animation
if (currentAnim.callback) {
currentAnim.callback(true /* finished */);
}
currentAnim.finished = true;
animation.animationIndex = findNextNonReducedMotionAnimationIndex(animation.animationIndex + 1);
if (animation.animationIndex < animations.length) {
const nextAnim = animations[animation.animationIndex];
nextAnim.onStart(nextAnim, currentAnim.current, now, currentAnim);
return false;
}
return true;
}
return false;
}
function onStart(animation, value, now, previousAnimation) {
// child animations inherit the setting, unless they already have it defined
// they will have it defined only if the user used the `reduceMotion` prop
animations.forEach(anim => {
if (anim.reduceMotion === undefined) {
anim.reduceMotion = animation.reduceMotion;
}
});
animation.animationIndex = findNextNonReducedMotionAnimationIndex(0);
if (previousAnimation === undefined) {
previousAnimation = animations[animations.length - 1];
}
const currentAnimation = animations[animation.animationIndex];
currentAnimation.onStart(currentAnimation, value, now, previousAnimation);
}
return {
isHigherOrder: true,
onFrame: sequence,
onStart,
animationIndex: 0,
current: animations[0].current,
callback,
reduceMotion: getReduceMotionForAnimation(reduceMotion)
};
});
}
//# sourceMappingURL=sequence.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,174 @@
'use strict';
import { defineAnimation, getReduceMotionForAnimation } from './util';
import { initialCalculations, calculateNewMassToMatchDuration, underDampedSpringCalculations, criticallyDampedSpringCalculations, isAnimationTerminatingCalculation, scaleZetaToMatchClamps, checkIfConfigIsValid } from './springUtils';
// TODO TYPESCRIPT This is a temporary type to get rid of .d.ts file.
/**
* Lets you create spring-based animations.
*
* @param toValue - the value at which the animation will come to rest - {@link AnimatableValue}
* @param config - the spring animation configuration - {@link SpringConfig}
* @param callback - a function called on animation complete - {@link AnimationCallback}
* @returns an [animation object](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary#animation-object) which holds the current state of the animation
* @see https://docs.swmansion.com/react-native-reanimated/docs/animations/withSpring
*/
export const withSpring = (toValue, userConfig, callback) => {
'worklet';
return defineAnimation(toValue, () => {
'worklet';
const defaultConfig = {
damping: 10,
mass: 1,
stiffness: 100,
overshootClamping: false,
restDisplacementThreshold: 0.01,
restSpeedThreshold: 2,
velocity: 0,
duration: 2000,
dampingRatio: 0.5,
reduceMotion: undefined,
clamp: undefined
};
const config = {
...defaultConfig,
...userConfig,
useDuration: !!(userConfig !== null && userConfig !== void 0 && userConfig.duration || userConfig !== null && userConfig !== void 0 && userConfig.dampingRatio),
skipAnimation: false
};
config.skipAnimation = !checkIfConfigIsValid(config);
if (config.duration === 0) {
config.skipAnimation = true;
}
function springOnFrame(animation, now) {
// eslint-disable-next-line @typescript-eslint/no-shadow
const {
toValue,
startTimestamp,
current
} = animation;
const timeFromStart = now - startTimestamp;
if (config.useDuration && timeFromStart >= config.duration) {
animation.current = toValue;
// clear lastTimestamp to avoid using stale value by the next spring animation that starts after this one
animation.lastTimestamp = 0;
return true;
}
if (config.skipAnimation) {
animation.current = toValue;
animation.lastTimestamp = 0;
return true;
}
const {
lastTimestamp,
velocity
} = animation;
const deltaTime = Math.min(now - lastTimestamp, 64);
animation.lastTimestamp = now;
const t = deltaTime / 1000;
const v0 = -velocity;
const x0 = toValue - current;
const {
zeta,
omega0,
omega1
} = animation;
const {
position: newPosition,
velocity: newVelocity
} = zeta < 1 ? underDampedSpringCalculations(animation, {
zeta,
v0,
x0,
omega0,
omega1,
t
}) : criticallyDampedSpringCalculations(animation, {
v0,
x0,
omega0,
t
});
animation.current = newPosition;
animation.velocity = newVelocity;
const {
isOvershooting,
isVelocity,
isDisplacement
} = isAnimationTerminatingCalculation(animation, config);
const springIsNotInMove = isOvershooting || isVelocity && isDisplacement;
if (!config.useDuration && springIsNotInMove) {
animation.velocity = 0;
animation.current = toValue;
// clear lastTimestamp to avoid using stale value by the next spring animation that starts after this one
animation.lastTimestamp = 0;
return true;
}
return false;
}
function isTriggeredTwice(previousAnimation, animation) {
return (previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.lastTimestamp) && (previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.startTimestamp) && (previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.toValue) === animation.toValue && (previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.duration) === animation.duration && (previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.dampingRatio) === animation.dampingRatio;
}
function onStart(animation, value, now, previousAnimation) {
animation.current = value;
animation.startValue = value;
let mass = config.mass;
const triggeredTwice = isTriggeredTwice(previousAnimation, animation);
const duration = config.duration;
const x0 = triggeredTwice ? // If animation is triggered twice we want to continue the previous animation
// form the previous starting point
previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.startValue : Number(animation.toValue) - value;
if (previousAnimation) {
animation.velocity = (triggeredTwice ? previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.velocity : (previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.velocity) + config.velocity) || 0;
} else {
animation.velocity = config.velocity || 0;
}
if (triggeredTwice) {
animation.zeta = (previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.zeta) || 0;
animation.omega0 = (previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.omega0) || 0;
animation.omega1 = (previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.omega1) || 0;
} else {
if (config.useDuration) {
const actualDuration = triggeredTwice ?
// If animation is triggered twice we want to continue the previous animation
// so we need to include the time that already elapsed
duration - (((previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.lastTimestamp) || 0) - ((previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.startTimestamp) || 0)) : duration;
config.duration = actualDuration;
mass = calculateNewMassToMatchDuration(x0, config, animation.velocity);
}
const {
zeta,
omega0,
omega1
} = initialCalculations(mass, config);
animation.zeta = zeta;
animation.omega0 = omega0;
animation.omega1 = omega1;
if (config.clamp !== undefined) {
animation.zeta = scaleZetaToMatchClamps(animation, config.clamp);
}
}
animation.lastTimestamp = (previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.lastTimestamp) || now;
animation.startTimestamp = triggeredTwice ? (previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.startTimestamp) || now : now;
}
return {
onFrame: springOnFrame,
onStart,
toValue,
velocity: config.velocity || 0,
current: toValue,
startValue: 0,
callback,
lastTimestamp: 0,
startTimestamp: 0,
zeta: 0,
omega0: 0,
omega1: 0,
reduceMotion: getReduceMotionForAnimation(config.reduceMotion)
};
});
};
//# sourceMappingURL=spring.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,270 @@
'use strict';
/**
* Spring animation configuration.
*
* @param mass - The weight of the spring. Reducing this value makes the animation faster. Defaults to 1.
* @param damping - How quickly a spring slows down. Higher damping means the spring will come to rest faster. Defaults to 10.
* @param duration - Length of the animation (in milliseconds). Defaults to 2000.
* @param dampingRatio - How damped the spring is. Value 1 means the spring is critically damped, and value \>1 means the spring is overdamped. Defaults to 0.5.
* @param stiffness - How bouncy the spring is. Defaults to 100.
* @param velocity - Initial velocity applied to the spring equation. Defaults to 0.
* @param overshootClamping - Whether a spring can bounce over the `toValue`. Defaults to false.
* @param restDisplacementThreshold - The displacement below which the spring will snap to toValue without further oscillations. Defaults to 0.01.
* @param restSpeedThreshold - The speed in pixels per second from which the spring will snap to toValue without further oscillations. Defaults to 2.
* @param reduceMotion - Determines how the animation responds to the device's reduced motion accessibility setting. Default to `ReduceMotion.System` - {@link ReduceMotion}.
* @see https://docs.swmansion.com/react-native-reanimated/docs/animations/withSpring/#config-
*/
// This type contains all the properties from SpringConfig, which are changed to be required,
// except for optional 'reduceMotion' and 'clamp'
export function checkIfConfigIsValid(config) {
'worklet';
var _config$clamp, _config$clamp2;
let errorMessage = '';
['stiffness', 'damping', 'dampingRatio', 'restDisplacementThreshold', 'restSpeedThreshold', 'mass'].forEach(prop => {
const value = config[prop];
if (value <= 0) {
errorMessage += `, ${prop} must be grater than zero but got ${value}`;
}
});
if (config.duration < 0) {
errorMessage += `, duration can't be negative, got ${config.duration}`;
}
if ((_config$clamp = config.clamp) !== null && _config$clamp !== void 0 && _config$clamp.min && (_config$clamp2 = config.clamp) !== null && _config$clamp2 !== void 0 && _config$clamp2.max && config.clamp.min > config.clamp.max) {
errorMessage += `, clamp.min should be lower than clamp.max, got clamp: {min: ${config.clamp.min}, max: ${config.clamp.max}} `;
}
if (errorMessage !== '') {
console.warn('[Reanimated] Invalid spring config' + errorMessage);
}
return errorMessage === '';
}
// ts-prune-ignore-next This function is exported to be tested
export function bisectRoot({
min,
max,
func,
maxIterations = 20
}) {
'worklet';
const ACCURACY = 0.00005;
let idx = maxIterations;
let current = (max + min) / 2;
while (Math.abs(func(current)) > ACCURACY && idx > 0) {
idx -= 1;
if (func(current) < 0) {
min = current;
} else {
max = current;
}
current = (min + max) / 2;
}
return current;
}
export function initialCalculations(mass = 0, config) {
'worklet';
if (config.skipAnimation) {
return {
zeta: 0,
omega0: 0,
omega1: 0
};
}
if (config.useDuration) {
const {
stiffness: k,
dampingRatio: zeta
} = config;
/** omega0 and omega1 denote angular frequency and natural angular frequency, see this link for formulas:
* https://courses.lumenlearning.com/suny-osuniversityphysics/chapter/15-5-damped-oscillations/
*/
const omega0 = Math.sqrt(k / mass);
const omega1 = omega0 * Math.sqrt(1 - zeta ** 2);
return {
zeta,
omega0,
omega1
};
} else {
const {
damping: c,
mass: m,
stiffness: k
} = config;
const zeta = c / (2 * Math.sqrt(k * m)); // damping ratio
const omega0 = Math.sqrt(k / m); // undamped angular frequency of the oscillator (rad/ms)
const omega1 = omega0 * Math.sqrt(1 - zeta ** 2); // exponential decay
return {
zeta,
omega0,
omega1
};
}
}
/** We make an assumption that we can manipulate zeta without changing duration of movement.
* According to theory this change is small and tests shows that we can indeed ignore it.
*/
export function scaleZetaToMatchClamps(animation, clamp) {
'worklet';
const {
zeta,
toValue,
startValue
} = animation;
const toValueNum = Number(toValue);
if (toValueNum === startValue) {
return zeta;
}
const [firstBound, secondBound] = toValueNum - startValue > 0 ? [clamp.min, clamp.max] : [clamp.max, clamp.min];
/** The extrema we get from equation below are relative (we obtain a ratio),
* To get absolute extrema we convert it as follows:
*
* AbsoluteExtremum = startValue ± RelativeExtremum * (toValue - startValue)
* Where ± denotes:
* + if extremum is over the target
* - otherwise
*/
const relativeExtremum1 = secondBound !== undefined ? Math.abs((secondBound - toValueNum) / (toValueNum - startValue)) : undefined;
const relativeExtremum2 = firstBound !== undefined ? Math.abs((firstBound - toValueNum) / (toValueNum - startValue)) : undefined;
/** Use this formula http://hyperphysics.phy-astr.gsu.edu/hbase/oscda.html to calculate
* first two extrema. These extrema are located where cos = +- 1
*
* Therefore the first two extrema are:
*
* Math.exp(-zeta * Math.PI); (over the target)
* Math.exp(-zeta * 2 * Math.PI); (before the target)
*/
const newZeta1 = relativeExtremum1 !== undefined ? Math.abs(Math.log(relativeExtremum1) / Math.PI) : undefined;
const newZeta2 = relativeExtremum2 !== undefined ? Math.abs(Math.log(relativeExtremum2) / (2 * Math.PI)) : undefined;
const zetaSatisfyingClamp = [newZeta1, newZeta2].filter(x => x !== undefined);
// The bigger is zeta the smaller are bounces, we return the biggest one
// because it should satisfy all conditions
return Math.max(...zetaSatisfyingClamp, zeta);
}
/** Runs before initial */
export function calculateNewMassToMatchDuration(x0, config, v0) {
'worklet';
if (config.skipAnimation) {
return 0;
}
/** Use this formula: https://phys.libretexts.org/Bookshelves/University_Physics/Book%3A_University_Physics_(OpenStax)/Book%3A_University_Physics_I_-_Mechanics_Sound_Oscillations_and_Waves_(OpenStax)/15%3A_Oscillations/15.06%3A_Damped_Oscillations
* to find the asymptote and estimate the damping that gives us the expected duration
⎛ ⎛ c⎞ ⎞
⎜-⎜──⎟ ⋅ duration⎟
⎝ ⎝2m⎠ ⎠
A ⋅ e = threshold
Amplitude calculated using "Conservation of energy"
_________________
2 2
m ⋅ v0 + k ⋅ x0
amplitude = ─────────────────
╲╱ k
And replace mass with damping ratio which is provided: m = (c^2)/(4 * k * zeta^2)
*/
const {
stiffness: k,
dampingRatio: zeta,
restSpeedThreshold: threshold,
duration
} = config;
const durationForMass = mass => {
'worklet';
const amplitude = (mass * v0 * v0 + k * x0 * x0) / (Math.exp(1 - 0.5 * zeta) * k);
const c = zeta * 2 * Math.sqrt(k * mass);
return 1000 * (-2 * mass / c) * Math.log(threshold * 0.01 / amplitude) - duration;
};
// Bisection turns out to be much faster than Newton's method in our case
return bisectRoot({
min: 0,
max: 100,
func: durationForMass
});
}
export function criticallyDampedSpringCalculations(animation, precalculatedValues) {
'worklet';
const {
toValue
} = animation;
const {
v0,
x0,
omega0,
t
} = precalculatedValues;
const criticallyDampedEnvelope = Math.exp(-omega0 * t);
const criticallyDampedPosition = toValue - criticallyDampedEnvelope * (x0 + (v0 + omega0 * x0) * t);
const criticallyDampedVelocity = criticallyDampedEnvelope * (v0 * (t * omega0 - 1) + t * x0 * omega0 * omega0);
return {
position: criticallyDampedPosition,
velocity: criticallyDampedVelocity
};
}
export function underDampedSpringCalculations(animation, precalculatedValues) {
'worklet';
const {
toValue,
current,
velocity
} = animation;
const {
zeta,
t,
omega0,
omega1
} = precalculatedValues;
const v0 = -velocity;
const x0 = toValue - current;
const sin1 = Math.sin(omega1 * t);
const cos1 = Math.cos(omega1 * t);
// under damped
const underDampedEnvelope = Math.exp(-zeta * omega0 * t);
const underDampedFrag1 = underDampedEnvelope * (sin1 * ((v0 + zeta * omega0 * x0) / omega1) + x0 * cos1);
const underDampedPosition = toValue - underDampedFrag1;
// This looks crazy -- it's actually just the derivative of the oscillation function
const underDampedVelocity = zeta * omega0 * underDampedFrag1 - underDampedEnvelope * (cos1 * (v0 + zeta * omega0 * x0) - omega1 * x0 * sin1);
return {
position: underDampedPosition,
velocity: underDampedVelocity
};
}
export function isAnimationTerminatingCalculation(animation, config) {
'worklet';
const {
toValue,
velocity,
startValue,
current
} = animation;
const isOvershooting = config.overshootClamping ? current > toValue && startValue < toValue || current < toValue && startValue > toValue : false;
const isVelocity = Math.abs(velocity) < config.restSpeedThreshold;
const isDisplacement = Math.abs(toValue - current) < config.restDisplacementThreshold;
return {
isOvershooting,
isVelocity,
isDisplacement
};
}
//# sourceMappingURL=springUtils.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,173 @@
'use strict';
import { defineAnimation } from './util';
import { withTiming } from './timing';
import { ColorProperties, processColor } from '../Colors';
// resolves path to value for nested objects
// if path cannot be resolved returns undefined
function resolvePath(obj, path) {
'worklet';
const keys = Array.isArray(path) ? path : [path];
return keys.reduce((acc, current) => {
if (Array.isArray(acc) && typeof current === 'number') {
return acc[current];
} else if (acc !== null && typeof acc === 'object' && current in acc) {
return acc[current];
}
return undefined;
}, obj);
}
// set value at given path
function setPath(obj, path, value) {
'worklet';
const keys = Array.isArray(path) ? path : [path];
let currObj = obj;
for (let i = 0; i < keys.length - 1; i++) {
// creates entry if there isn't one
currObj = currObj;
if (!(keys[i] in currObj)) {
// if next key is a number create an array
if (typeof keys[i + 1] === 'number') {
currObj[keys[i]] = [];
} else {
currObj[keys[i]] = {};
}
}
currObj = currObj[keys[i]];
}
currObj[keys[keys.length - 1]] = value;
}
export function withStyleAnimation(styleAnimations) {
'worklet';
return defineAnimation({}, () => {
'worklet';
const onFrame = (animation, now) => {
let stillGoing = false;
const entriesToCheck = [{
value: animation.styleAnimations,
path: []
}];
while (entriesToCheck.length > 0) {
const currentEntry = entriesToCheck.pop();
if (Array.isArray(currentEntry.value)) {
for (let index = 0; index < currentEntry.value.length; index++) {
entriesToCheck.push({
value: currentEntry.value[index],
path: currentEntry.path.concat(index)
});
}
} else if (typeof currentEntry.value === 'object' && currentEntry.value.onFrame === undefined) {
// nested object
for (const key of Object.keys(currentEntry.value)) {
entriesToCheck.push({
value: currentEntry.value[key],
path: currentEntry.path.concat(key)
});
}
} else {
const currentStyleAnimation = currentEntry.value;
if (currentStyleAnimation.finished) {
continue;
}
const finished = currentStyleAnimation.onFrame(currentStyleAnimation, now);
if (finished) {
currentStyleAnimation.finished = true;
if (currentStyleAnimation.callback) {
currentStyleAnimation.callback(true);
}
} else {
stillGoing = true;
}
// When working with animations changing colors, we need to make sure that each one of them begins with a rgba, not a processed number.
// Thus, we only set the path to a processed color, but currentStyleAnimation.current stays as rgba.
const isAnimatingColorProp = ColorProperties.includes(currentEntry.path[0]);
setPath(animation.current, currentEntry.path, isAnimatingColorProp ? processColor(currentStyleAnimation.current) : currentStyleAnimation.current);
}
}
return !stillGoing;
};
const onStart = (animation, value, now, previousAnimation) => {
const entriesToCheck = [{
value: styleAnimations,
path: []
}];
while (entriesToCheck.length > 0) {
const currentEntry = entriesToCheck.pop();
if (Array.isArray(currentEntry.value)) {
for (let index = 0; index < currentEntry.value.length; index++) {
entriesToCheck.push({
value: currentEntry.value[index],
path: currentEntry.path.concat(index)
});
}
} else if (typeof currentEntry.value === 'object' && currentEntry.value.onStart === undefined) {
for (const key of Object.keys(currentEntry.value)) {
entriesToCheck.push({
value: currentEntry.value[key],
path: currentEntry.path.concat(key)
});
}
} else {
const prevAnimation = resolvePath(previousAnimation === null || previousAnimation === void 0 ? void 0 : previousAnimation.styleAnimations, currentEntry.path);
let prevVal = resolvePath(value, currentEntry.path);
if (prevAnimation && !prevVal) {
prevVal = prevAnimation.current;
}
if (prevVal === undefined) {
console.warn(`Initial values for animation are missing for property ${currentEntry.path.join('.')}`);
}
setPath(animation.current, currentEntry.path, prevVal);
let currentAnimation;
if (typeof currentEntry.value !== 'object' || !currentEntry.value.onStart) {
currentAnimation = withTiming(currentEntry.value, {
duration: 0
}); // TODO TYPESCRIPT this temporary cast is to get rid of .d.ts file.
setPath(animation.styleAnimations, currentEntry.path, currentAnimation);
} else {
currentAnimation = currentEntry.value;
}
currentAnimation.onStart(currentAnimation, prevVal, now, prevAnimation);
}
}
};
const callback = finished => {
if (!finished) {
const animationsToCheck = [styleAnimations];
while (animationsToCheck.length > 0) {
const currentAnimation = animationsToCheck.pop();
if (Array.isArray(currentAnimation)) {
for (const element of currentAnimation) {
animationsToCheck.push(element);
}
} else if (typeof currentAnimation === 'object' && currentAnimation.onStart === undefined) {
for (const value of Object.values(currentAnimation)) {
animationsToCheck.push(value);
}
} else {
const currentStyleAnimation = currentAnimation;
if (!currentStyleAnimation.finished && currentStyleAnimation.callback) {
currentStyleAnimation.callback(false);
}
}
}
}
};
return {
isHigherOrder: true,
onFrame,
onStart,
current: {},
styleAnimations,
callback
};
});
}
//# sourceMappingURL=styleAnimation.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,93 @@
'use strict';
import { Easing } from '../Easing';
import { assertEasingIsWorklet, defineAnimation, getReduceMotionForAnimation } from './util';
/**
* The timing animation configuration.
*
* @param duration - Length of the animation (in milliseconds). Defaults to 300.
* @param easing - An easing function which defines the animation curve. Defaults to `Easing.inOut(Easing.quad)`.
* @param reduceMotion - Determines how the animation responds to the device's reduced motion accessibility setting. Default to `ReduceMotion.System` - {@link ReduceMotion}.
* @see https://docs.swmansion.com/react-native-reanimated/docs/animations/withTiming#config-
*/
// TODO TYPESCRIPT This is temporary type put in here to get rid of our .d.ts file
/**
* Lets you create an animation based on duration and easing.
*
* @param toValue - The value on which the animation will come at rest - {@link AnimatableValue}.
* @param config - The timing animation configuration - {@link TimingConfig}.
* @param callback - A function called on animation complete - {@link AnimationCallback}.
* @returns An [animation object](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary#animation-object) which holds the current state of the animation.
* @see https://docs.swmansion.com/react-native-reanimated/docs/animations/withTiming
*/
export const withTiming = function (toValue, userConfig, callback) {
'worklet';
if (__DEV__ && userConfig !== null && userConfig !== void 0 && userConfig.easing) {
assertEasingIsWorklet(userConfig.easing);
}
return defineAnimation(toValue, () => {
'worklet';
const config = {
duration: 300,
easing: Easing.inOut(Easing.quad)
};
if (userConfig) {
Object.keys(userConfig).forEach(key => config[key] = userConfig[key]);
}
function timing(animation, now) {
// eslint-disable-next-line @typescript-eslint/no-shadow
const {
toValue,
startTime,
startValue
} = animation;
const runtime = now - startTime;
if (runtime >= config.duration) {
// reset startTime to avoid reusing finished animation config in `start` method
animation.startTime = 0;
animation.current = toValue;
return true;
}
const progress = animation.easing(runtime / config.duration);
animation.current = startValue + (toValue - startValue) * progress;
return false;
}
function onStart(animation, value, now, previousAnimation) {
if (previousAnimation && previousAnimation.type === 'timing' && previousAnimation.toValue === toValue && previousAnimation.startTime) {
// to maintain continuity of timing animations we check if we are starting
// new timing over the old one with the same parameters. If so, we want
// to copy animation timeline properties
animation.startTime = previousAnimation.startTime;
animation.startValue = previousAnimation.startValue;
} else {
animation.startTime = now;
animation.startValue = value;
}
animation.current = value;
if (typeof config.easing === 'object') {
animation.easing = config.easing.factory();
} else {
animation.easing = config.easing;
}
}
return {
type: 'timing',
onFrame: timing,
onStart: onStart,
progress: 0,
toValue,
startValue: 0,
startTime: 0,
easing: () => 0,
current: toValue,
callback,
reduceMotion: getReduceMotionForAnimation(userConfig === null || userConfig === void 0 ? void 0 : userConfig.reduceMotion)
};
});
};
//# sourceMappingURL=timing.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,200 @@
'use strict';
export function isAffineMatrixFlat(x) {
'worklet';
return Array.isArray(x) && x.length === 16 && x.every(element => typeof element === 'number' && !isNaN(element));
}
// ts-prune-ignore-next This function is exported to be tested
export function isAffineMatrix(x) {
'worklet';
return Array.isArray(x) && x.length === 4 && x.every(row => Array.isArray(row) && row.length === 4 && row.every(element => typeof element === 'number' && !isNaN(element)));
}
export function flatten(matrix) {
'worklet';
return matrix.flat();
}
// ts-prune-ignore-next This function is exported to be tested
export function unflatten(m) {
'worklet';
return [[m[0], m[1], m[2], m[3]], [m[4], m[5], m[6], m[7]], [m[8], m[9], m[10], m[11]], [m[12], m[13], m[14], m[15]]];
}
function maybeFlattenMatrix(matrix) {
'worklet';
return isAffineMatrix(matrix) ? flatten(matrix) : matrix;
}
export function multiplyMatrices(a, b) {
'worklet';
return [[a[0][0] * b[0][0] + a[0][1] * b[1][0] + a[0][2] * b[2][0] + a[0][3] * b[3][0], a[0][0] * b[0][1] + a[0][1] * b[1][1] + a[0][2] * b[2][1] + a[0][3] * b[3][1], a[0][0] * b[0][2] + a[0][1] * b[1][2] + a[0][2] * b[2][2] + a[0][3] * b[3][2], a[0][0] * b[0][3] + a[0][1] * b[1][3] + a[0][2] * b[2][3] + a[0][3] * b[3][3]], [a[1][0] * b[0][0] + a[1][1] * b[1][0] + a[1][2] * b[2][0] + a[1][3] * b[3][0], a[1][0] * b[0][1] + a[1][1] * b[1][1] + a[1][2] * b[2][1] + a[1][3] * b[3][1], a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2] * b[2][2] + a[1][3] * b[3][2], a[1][0] * b[0][3] + a[1][1] * b[1][3] + a[1][2] * b[2][3] + a[1][3] * b[3][3]], [a[2][0] * b[0][0] + a[2][1] * b[1][0] + a[2][2] * b[2][0] + a[2][3] * b[3][0], a[2][0] * b[0][1] + a[2][1] * b[1][1] + a[2][2] * b[2][1] + a[2][3] * b[3][1], a[2][0] * b[0][2] + a[2][1] * b[1][2] + a[2][2] * b[2][2] + a[2][3] * b[3][2], a[2][0] * b[0][3] + a[2][1] * b[1][3] + a[2][2] * b[2][3] + a[2][3] * b[3][3]], [a[3][0] * b[0][0] + a[3][1] * b[1][0] + a[3][2] * b[2][0] + a[3][3] * b[3][0], a[3][0] * b[0][1] + a[3][1] * b[1][1] + a[3][2] * b[2][1] + a[3][3] * b[3][1], a[3][0] * b[0][2] + a[3][1] * b[1][2] + a[3][2] * b[2][2] + a[3][3] * b[3][2], a[3][0] * b[0][3] + a[3][1] * b[1][3] + a[3][2] * b[2][3] + a[3][3] * b[3][3]]];
}
export function subtractMatrices(maybeFlatA, maybeFlatB) {
'worklet';
const isFlatOnStart = isAffineMatrixFlat(maybeFlatA);
const a = maybeFlattenMatrix(maybeFlatA);
const b = maybeFlattenMatrix(maybeFlatB);
const c = a.map((_, i) => a[i] - b[i]);
return isFlatOnStart ? c : unflatten(c);
}
export function addMatrices(maybeFlatA, maybeFlatB) {
'worklet';
const isFlatOnStart = isAffineMatrixFlat(maybeFlatA);
const a = maybeFlattenMatrix(maybeFlatA);
const b = maybeFlattenMatrix(maybeFlatB);
const c = a.map((_, i) => a[i] + b[i]);
return isFlatOnStart ? c : unflatten(c);
}
export function scaleMatrix(maybeFlatA, scalar) {
'worklet';
const isFlatOnStart = isAffineMatrixFlat(maybeFlatA);
const a = maybeFlattenMatrix(maybeFlatA);
const b = a.map(x => x * scalar);
return isFlatOnStart ? b : unflatten(b);
}
export function getRotationMatrix(angle, axis = 'z') {
'worklet';
const cos = Math.cos(angle);
const sin = Math.sin(angle);
switch (axis) {
case 'z':
return [[cos, sin, 0, 0], [-sin, cos, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
case 'y':
return [[cos, 0, -sin, 0], [0, 1, 0, 0], [sin, 0, cos, 0], [0, 0, 0, 1]];
case 'x':
return [[1, 0, 0, 0], [0, cos, sin, 0], [0, -sin, cos, 0], [0, 0, 0, 1]];
}
}
function norm3d(x, y, z) {
'worklet';
return Math.sqrt(x * x + y * y + z * z);
}
function transposeMatrix(matrix) {
'worklet';
const m = flatten(matrix);
return [[m[0], m[4], m[8], m[12]], [m[1], m[5], m[9], m[13]], [m[2], m[6], m[10], m[14]], [m[3], m[7], m[11], m[15]]];
}
function assertVectorsHaveEqualLengths(a, b) {
'worklet';
if (__DEV__ && a.length !== b.length) {
throw new Error(`[Reanimated] Cannot calculate inner product of two vectors of different lengths. Length of ${a.toString()} is ${a.length} and length of ${b.toString()} is ${b.length}.`);
}
}
function innerProduct(a, b) {
'worklet';
assertVectorsHaveEqualLengths(a, b);
return a.reduce((acc, _, i) => acc + a[i] * b[i], 0);
}
function projection(u, a) {
'worklet';
assertVectorsHaveEqualLengths(u, a);
const s = innerProduct(u, a) / innerProduct(u, u);
return u.map(e => e * s);
}
function subtractVectors(a, b) {
'worklet';
assertVectorsHaveEqualLengths(a, b);
return a.map((_, i) => a[i] - b[i]);
}
function scaleVector(u, a) {
'worklet';
return u.map(e => e * a);
}
function gramSchmidtAlgorithm(matrix) {
// Gram-Schmidt orthogonalization decomposes any matrix with non-zero determinant into an orthogonal and a triangular matrix
// These matrices are equal to rotation and skew matrices respectively, because we apply it to transformation matrix
// That is expected to already have extracted the remaining transforms (scale & translation)
'worklet';
const [a0, a1, a2, a3] = matrix;
const u0 = a0;
const u1 = subtractVectors(a1, projection(u0, a1));
const u2 = subtractVectors(subtractVectors(a2, projection(u0, a2)), projection(u1, a2));
const u3 = subtractVectors(subtractVectors(subtractVectors(a3, projection(u0, a3)), projection(u1, a3)), projection(u2, a3));
const [e0, e1, e2, e3] = [u0, u1, u2, u3].map(u => scaleVector(u, 1 / Math.sqrt(innerProduct(u, u))));
const rotationMatrix = [[e0[0], e1[0], e2[0], e3[0]], [e0[1], e1[1], e2[1], e3[1]], [e0[2], e1[2], e2[2], e3[2]], [e0[3], e1[3], e2[3], e3[3]]];
const skewMatrix = [[innerProduct(e0, a0), innerProduct(e0, a1), innerProduct(e0, a2), innerProduct(e0, a3)], [0, innerProduct(e1, a1), innerProduct(e1, a2), innerProduct(e1, a3)], [0, 0, innerProduct(e2, a2), innerProduct(e2, a3)], [0, 0, 0, innerProduct(e3, a3)]];
return {
rotationMatrix: transposeMatrix(rotationMatrix),
skewMatrix: transposeMatrix(skewMatrix)
};
}
// ts-prune-ignore-next This function is exported to be tested
export function decomposeMatrix(unknownTypeMatrix) {
'worklet';
const matrix = maybeFlattenMatrix(unknownTypeMatrix);
// normalize matrix
if (matrix[15] === 0) {
throw new Error('[Reanimated] Invalid transform matrix.');
}
matrix.forEach((_, i) => matrix[i] /= matrix[15]);
const translationMatrix = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [matrix[12], matrix[13], matrix[14], 1]];
const sx = matrix[15] * norm3d(matrix[0], matrix[4], matrix[8]);
const sy = matrix[15] * norm3d(matrix[1], matrix[5], matrix[9]);
const sz = matrix[15] * norm3d(matrix[2], matrix[6], matrix[10]);
// eslint-disable-next-line @typescript-eslint/no-shadow
const scaleMatrix = [[sx, 0, 0, 0], [0, sy, 0, 0], [0, 0, sz, 0], [0, 0, 0, 1]];
const rotationAndSkewMatrix = [[matrix[0] / sx, matrix[1] / sx, matrix[2] / sx, 0], [matrix[4] / sy, matrix[5] / sy, matrix[6] / sy, 0], [matrix[8] / sz, matrix[9] / sz, matrix[10] / sz, 0], [0, 0, 0, 1]];
const {
rotationMatrix,
skewMatrix
} = gramSchmidtAlgorithm(rotationAndSkewMatrix);
return {
translationMatrix,
scaleMatrix,
rotationMatrix,
skewMatrix
};
}
export function decomposeMatrixIntoMatricesAndAngles(matrix) {
'worklet';
// eslint-disable-next-line @typescript-eslint/no-shadow
const {
scaleMatrix,
rotationMatrix,
translationMatrix,
skewMatrix
} = decomposeMatrix(matrix);
const sinRy = -rotationMatrix[0][2];
const ry = Math.asin(sinRy);
let rx;
let rz;
if (sinRy === 1 || sinRy === -1) {
rz = 0;
rx = Math.atan2(sinRy * rotationMatrix[0][1], sinRy * rotationMatrix[0][2]);
} else {
rz = Math.atan2(rotationMatrix[0][1], rotationMatrix[0][0]);
rx = Math.atan2(rotationMatrix[1][2], rotationMatrix[2][2]);
}
return {
scaleMatrix,
rotationMatrix,
translationMatrix,
skewMatrix,
rx: rx || 0,
ry: ry || 0,
rz: rz || 0
};
}
//# sourceMappingURL=matrixUtils.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,341 @@
/* eslint-disable @typescript-eslint/no-shadow */
'use strict';
import { isColor, convertToRGBA, rgbaArrayToRGBAColor, toGammaSpace, toLinearSpace } from '../Colors';
import { ReduceMotion, isWorkletFunction } from '../commonTypes';
import { flatten, multiplyMatrices, scaleMatrix, addMatrices, decomposeMatrixIntoMatricesAndAngles, isAffineMatrixFlat, subtractMatrices, getRotationMatrix } from './transformationMatrix/matrixUtils';
import { isReducedMotion, shouldBeUseWeb } from '../PlatformChecker';
let IN_STYLE_UPDATER = false;
const IS_REDUCED_MOTION = isReducedMotion();
const SHOULD_BE_USE_WEB = shouldBeUseWeb();
if (__DEV__ && IS_REDUCED_MOTION) {
console.warn(`[Reanimated] Reduced motion setting is enabled on this device. This warning is visible only in the development mode. Some animations will be disabled by default. You can override the behavior for individual animations, see https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#reduced-motion-setting-is-enabled-on-this-device.`);
}
export function assertEasingIsWorklet(easing) {
'worklet';
if (_WORKLET) {
// If this is called on UI (for example from gesture handler with worklets), we don't get easing,
// but its bound copy, which is not a worklet. We don't want to throw any error then.
return;
}
if (SHOULD_BE_USE_WEB) {
// It is possible to run reanimated on web without plugin, so let's skip this check on web
return;
}
// @ts-ignore typescript wants us to use `in` instead, which doesn't work with host objects
if (easing !== null && easing !== void 0 && easing.factory) {
return;
}
if (!isWorkletFunction(easing)) {
throw new Error('[Reanimated] The easing function is not a worklet. Please make sure you import `Easing` from react-native-reanimated.');
}
}
export function initialUpdaterRun(updater) {
IN_STYLE_UPDATER = true;
const result = updater();
IN_STYLE_UPDATER = false;
return result;
}
export function recognizePrefixSuffix(value) {
'worklet';
if (typeof value === 'string') {
const match = value.match(/([A-Za-z]*)(-?\d*\.?\d*)([eE][-+]?[0-9]+)?([A-Za-z%]*)/);
if (!match) {
throw new Error("[Reanimated] Couldn't parse animation value.");
}
const prefix = match[1];
const suffix = match[4];
// number with scientific notation
const number = match[2] + (match[3] ?? '');
return {
prefix,
suffix,
strippedValue: parseFloat(number)
};
} else {
return {
strippedValue: value
};
}
}
/**
* Returns whether the motion should be reduced for a specified config.
* By default returns the system setting.
*/
export function getReduceMotionFromConfig(config) {
'worklet';
return !config || config === ReduceMotion.System ? IS_REDUCED_MOTION : config === ReduceMotion.Always;
}
/**
* Returns the value that should be assigned to `animation.reduceMotion`
* for a given config. If the config is not defined, `undefined` is returned.
*/
export function getReduceMotionForAnimation(config) {
'worklet';
// if the config is not defined, we want `reduceMotion` to be undefined,
// so the parent animation knows if it should overwrite it
if (!config) {
return undefined;
}
return getReduceMotionFromConfig(config);
}
function applyProgressToMatrix(progress, a, b) {
'worklet';
return addMatrices(a, scaleMatrix(subtractMatrices(b, a), progress));
}
function applyProgressToNumber(progress, a, b) {
'worklet';
return a + progress * (b - a);
}
function decorateAnimation(animation) {
'worklet';
const baseOnStart = animation.onStart;
const baseOnFrame = animation.onFrame;
if (animation.isHigherOrder) {
animation.onStart = (animation, value, timestamp, previousAnimation) => {
if (animation.reduceMotion === undefined) {
animation.reduceMotion = getReduceMotionFromConfig();
}
return baseOnStart(animation, value, timestamp, previousAnimation);
};
return;
}
const animationCopy = Object.assign({}, animation);
delete animationCopy.callback;
const prefNumberSuffOnStart = (animation, value, timestamp, previousAnimation) => {
// recognize prefix, suffix, and updates stripped value on animation start
const {
prefix,
suffix,
strippedValue
} = recognizePrefixSuffix(value);
animation.__prefix = prefix;
animation.__suffix = suffix;
animation.strippedCurrent = strippedValue;
const {
strippedValue: strippedToValue
} = recognizePrefixSuffix(animation.toValue);
animation.current = strippedValue;
animation.startValue = strippedValue;
animation.toValue = strippedToValue;
if (previousAnimation && previousAnimation !== animation) {
const {
prefix: paPrefix,
suffix: paSuffix,
strippedValue: paStrippedValue
} = recognizePrefixSuffix(previousAnimation.current);
previousAnimation.current = paStrippedValue;
previousAnimation.__prefix = paPrefix;
previousAnimation.__suffix = paSuffix;
}
baseOnStart(animation, strippedValue, timestamp, previousAnimation);
animation.current = (animation.__prefix ?? '') + animation.current + (animation.__suffix ?? '');
if (previousAnimation && previousAnimation !== animation) {
previousAnimation.current = (previousAnimation.__prefix ?? '') +
// FIXME
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
previousAnimation.current + (previousAnimation.__suffix ?? '');
}
};
const prefNumberSuffOnFrame = (animation, timestamp) => {
animation.current = animation.strippedCurrent;
const res = baseOnFrame(animation, timestamp);
animation.strippedCurrent = animation.current;
animation.current = (animation.__prefix ?? '') + animation.current + (animation.__suffix ?? '');
return res;
};
const tab = ['R', 'G', 'B', 'A'];
const colorOnStart = (animation, value, timestamp, previousAnimation) => {
let RGBAValue;
let RGBACurrent;
let RGBAToValue;
const res = [];
if (isColor(value)) {
RGBACurrent = toLinearSpace(convertToRGBA(animation.current));
RGBAValue = toLinearSpace(convertToRGBA(value));
if (animation.toValue) {
RGBAToValue = toLinearSpace(convertToRGBA(animation.toValue));
}
}
tab.forEach((i, index) => {
animation[i] = Object.assign({}, animationCopy);
animation[i].current = RGBACurrent[index];
animation[i].toValue = RGBAToValue ? RGBAToValue[index] : undefined;
animation[i].onStart(animation[i], RGBAValue[index], timestamp, previousAnimation ? previousAnimation[i] : undefined);
res.push(animation[i].current);
});
animation.current = rgbaArrayToRGBAColor(toGammaSpace(res));
};
const colorOnFrame = (animation, timestamp) => {
const RGBACurrent = toLinearSpace(convertToRGBA(animation.current));
const res = [];
let finished = true;
tab.forEach((i, index) => {
animation[i].current = RGBACurrent[index];
const result = animation[i].onFrame(animation[i], timestamp);
// We really need to assign this value to result, instead of passing it directly - otherwise once "finished" is false, onFrame won't be called
finished = finished && result;
res.push(animation[i].current);
});
animation.current = rgbaArrayToRGBAColor(toGammaSpace(res));
return finished;
};
const transformationMatrixOnStart = (animation, value, timestamp, previousAnimation) => {
const toValue = animation.toValue;
animation.startMatrices = decomposeMatrixIntoMatricesAndAngles(value);
animation.stopMatrices = decomposeMatrixIntoMatricesAndAngles(toValue);
// We create an animation copy to animate single value between 0 and 100
// We set limits from 0 to 100 (instead of 0-1) to make spring look good
// with default thresholds.
animation[0] = Object.assign({}, animationCopy);
animation[0].current = 0;
animation[0].toValue = 100;
animation[0].onStart(animation[0], 0, timestamp, previousAnimation ? previousAnimation[0] : undefined);
animation.current = value;
};
const transformationMatrixOnFrame = (animation, timestamp) => {
let finished = true;
const result = animation[0].onFrame(animation[0], timestamp);
// We really need to assign this value to result, instead of passing it directly - otherwise once "finished" is false, onFrame won't be called
finished = finished && result;
const progress = animation[0].current / 100;
const transforms = ['translationMatrix', 'scaleMatrix', 'skewMatrix'];
const mappedTransforms = [];
transforms.forEach((key, _) => mappedTransforms.push(applyProgressToMatrix(progress, animation.startMatrices[key], animation.stopMatrices[key])));
const [currentTranslation, currentScale, skewMatrix] = mappedTransforms;
const rotations = ['x', 'y', 'z'];
const mappedRotations = [];
rotations.forEach((key, _) => {
const angle = applyProgressToNumber(progress, animation.startMatrices['r' + key], animation.stopMatrices['r' + key]);
mappedRotations.push(getRotationMatrix(angle, key));
});
const [rotationMatrixX, rotationMatrixY, rotationMatrixZ] = mappedRotations;
const rotationMatrix = multiplyMatrices(rotationMatrixX, multiplyMatrices(rotationMatrixY, rotationMatrixZ));
const updated = flatten(multiplyMatrices(multiplyMatrices(currentScale, multiplyMatrices(skewMatrix, rotationMatrix)), currentTranslation));
animation.current = updated;
return finished;
};
const arrayOnStart = (animation, value, timestamp, previousAnimation) => {
value.forEach((v, i) => {
animation[i] = Object.assign({}, animationCopy);
animation[i].current = v;
animation[i].toValue = animation.toValue[i];
animation[i].onStart(animation[i], v, timestamp, previousAnimation ? previousAnimation[i] : undefined);
});
animation.current = value;
};
const arrayOnFrame = (animation, timestamp) => {
let finished = true;
animation.current.forEach((_, i) => {
const result = animation[i].onFrame(animation[i], timestamp);
// We really need to assign this value to result, instead of passing it directly - otherwise once "finished" is false, onFrame won't be called
finished = finished && result;
animation.current[i] = animation[i].current;
});
return finished;
};
const objectOnStart = (animation, value, timestamp, previousAnimation) => {
for (const key in value) {
animation[key] = Object.assign({}, animationCopy);
animation[key].onStart = animation.onStart;
animation[key].current = value[key];
animation[key].toValue = animation.toValue[key];
animation[key].onStart(animation[key], value[key], timestamp, previousAnimation ? previousAnimation[key] : undefined);
}
animation.current = value;
};
const objectOnFrame = (animation, timestamp) => {
let finished = true;
const newObject = {};
for (const key in animation.current) {
const result = animation[key].onFrame(animation[key], timestamp);
// We really need to assign this value to result, instead of passing it directly - otherwise once "finished" is false, onFrame won't be called
finished = finished && result;
newObject[key] = animation[key].current;
}
animation.current = newObject;
return finished;
};
animation.onStart = (animation, value, timestamp, previousAnimation) => {
if (animation.reduceMotion === undefined) {
animation.reduceMotion = getReduceMotionFromConfig();
}
if (animation.reduceMotion) {
if (animation.toValue !== undefined) {
animation.current = animation.toValue;
} else {
// if there is no `toValue`, then the base function is responsible for setting the current value
baseOnStart(animation, value, timestamp, previousAnimation);
}
animation.startTime = 0;
animation.onFrame = () => true;
return;
}
if (isColor(value)) {
colorOnStart(animation, value, timestamp, previousAnimation);
animation.onFrame = colorOnFrame;
return;
} else if (isAffineMatrixFlat(value)) {
transformationMatrixOnStart(animation, value, timestamp, previousAnimation);
animation.onFrame = transformationMatrixOnFrame;
return;
} else if (Array.isArray(value)) {
arrayOnStart(animation, value, timestamp, previousAnimation);
animation.onFrame = arrayOnFrame;
return;
} else if (typeof value === 'string') {
prefNumberSuffOnStart(animation, value, timestamp, previousAnimation);
animation.onFrame = prefNumberSuffOnFrame;
return;
} else if (typeof value === 'object' && value !== null) {
objectOnStart(animation, value, timestamp, previousAnimation);
animation.onFrame = objectOnFrame;
return;
}
baseOnStart(animation, value, timestamp, previousAnimation);
};
}
export function defineAnimation(starting, factory) {
'worklet';
if (IN_STYLE_UPDATER) {
return starting;
}
const create = () => {
'worklet';
const animation = factory();
decorateAnimation(animation);
return animation;
};
if (_WORKLET || SHOULD_BE_USE_WEB) {
return create();
}
// @ts-ignore: eslint-disable-line
return create;
}
/**
* Lets you cancel a running animation paired to a shared value.
*
* @param sharedValue - The shared value of a running animation that you want to cancel.
* @see https://docs.swmansion.com/react-native-reanimated/docs/core/cancelAnimation
*/
export function cancelAnimation(sharedValue) {
'worklet';
// setting the current value cancels the animation if one is currently running
sharedValue.value = sharedValue.value; // eslint-disable-line no-self-assign
}
//# sourceMappingURL=util.js.map

File diff suppressed because one or more lines are too long