UNPKG

react-native-reanimated

Version:

More powerful alternative to Animated library for React Native.

438 lines (360 loc) • 13.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useAnimatedStyle = useAnimatedStyle; var _react = require("react"); var _core = require("../core"); var _UpdateProps = _interopRequireWildcard(require("../UpdateProps")); var _animation = require("../animation"); var _NativeReanimated = _interopRequireDefault(require("../NativeReanimated")); var _useSharedValue = require("./useSharedValue"); var _utils = require("./utils"); var _ViewDescriptorsSet = require("../ViewDescriptorsSet"); var _PlatformChecker = require("../PlatformChecker"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function prepareAnimation(frameTimestamp, animatedProp, lastAnimation, lastValue) { 'worklet'; if (Array.isArray(animatedProp)) { animatedProp.forEach((prop, index) => { prepareAnimation(frameTimestamp, prop, lastAnimation && lastAnimation[index], lastValue && lastValue[index]); }); // return animatedProp; } if (typeof animatedProp === 'object' && animatedProp.onFrame) { const animation = animatedProp; let value = animation.current; if (lastValue !== undefined) { if (typeof lastValue === 'object') { if (lastValue.value !== undefined) { // previously it was a shared value value = lastValue.value; } else if (lastValue.onFrame !== undefined) { if ((lastAnimation === null || lastAnimation === void 0 ? void 0 : lastAnimation.current) !== undefined) { // it was an animation before, copy its state value = lastAnimation.current; } else if ((lastValue === null || lastValue === void 0 ? void 0 : lastValue.current) !== undefined) { // it was initialized value = lastValue.current; } } } else { // previously it was a plain value, just set it as starting point value = lastValue; } } animation.callStart = timestamp => { animation.onStart(animation, value, timestamp, lastAnimation); }; animation.callStart(frameTimestamp); animation.callStart = null; } else if (typeof animatedProp === 'object') { // it is an object Object.keys(animatedProp).forEach(key => prepareAnimation(frameTimestamp, animatedProp[key], lastAnimation && lastAnimation[key], lastValue && lastValue[key])); } } function runAnimations(animation, timestamp, key, result, animationsActive) { 'worklet'; if (!animationsActive.value) { return true; } if (Array.isArray(animation)) { result[key] = []; let allFinished = true; animation.forEach((entry, index) => { if (!runAnimations(entry, timestamp, index, result[key], animationsActive)) { allFinished = false; } }); return allFinished; } else if (typeof animation === 'object' && animation.onFrame) { let finished = true; if (!animation.finished) { if (animation.callStart) { animation.callStart(timestamp); animation.callStart = null; } finished = animation.onFrame(animation, timestamp); animation.timestamp = timestamp; if (finished) { animation.finished = true; animation.callback && animation.callback(true /* finished */ ); } } result[key] = animation.current; return finished; } else if (typeof animation === 'object') { result[key] = {}; let allFinished = true; Object.keys(animation).forEach(k => { if (!runAnimations(animation[k], timestamp, k, result[key], animationsActive)) { allFinished = false; } }); return allFinished; } else { result[key] = animation; return true; } } function styleUpdater(viewDescriptors, updater, state, maybeViewRef, animationsActive) { 'worklet'; const animations = state.animations ?? {}; const newValues = updater() ?? {}; const oldValues = state.last; const nonAnimatedNewValues = {}; let hasAnimations = false; let frameTimestamp; let hasNonAnimatedValues = false; for (const key in newValues) { const value = newValues[key]; if ((0, _utils.isAnimated)(value)) { frameTimestamp = global.__frameTimestamp || performance.now(); prepareAnimation(frameTimestamp, value, animations[key], oldValues[key]); animations[key] = value; hasAnimations = true; } else { hasNonAnimatedValues = true; nonAnimatedNewValues[key] = value; delete animations[key]; } } if (hasAnimations) { const frame = timestamp => { const { animations, last, isAnimationCancelled } = state; if (isAnimationCancelled) { state.isAnimationRunning = false; return; } const updates = {}; let allFinished = true; for (const propName in animations) { const finished = runAnimations(animations[propName], timestamp, propName, updates, animationsActive); if (finished) { last[propName] = updates[propName]; delete animations[propName]; } else { allFinished = false; } } if (updates) { (0, _UpdateProps.default)(viewDescriptors, updates, maybeViewRef); } if (!allFinished) { requestAnimationFrame(frame); } else { state.isAnimationRunning = false; } }; state.animations = animations; if (!state.isAnimationRunning) { state.isAnimationCancelled = false; state.isAnimationRunning = true; frame(frameTimestamp); } if (hasNonAnimatedValues) { (0, _UpdateProps.default)(viewDescriptors, nonAnimatedNewValues, maybeViewRef); } } else { state.isAnimationCancelled = true; state.animations = []; if (!(0, _utils.shallowEqual)(oldValues, newValues)) { (0, _UpdateProps.default)(viewDescriptors, newValues, maybeViewRef); } } state.last = newValues; } function jestStyleUpdater(viewDescriptors, updater, state, maybeViewRef, animationsActive, animatedStyle) { 'worklet'; let adapters = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : []; const animations = state.animations ?? {}; const newValues = updater() ?? {}; const oldValues = state.last; // extract animated props let hasAnimations = false; let frameTimestamp; Object.keys(animations).forEach(key => { const value = newValues[key]; if (!(0, _utils.isAnimated)(value)) { delete animations[key]; } }); Object.keys(newValues).forEach(key => { const value = newValues[key]; if ((0, _utils.isAnimated)(value)) { frameTimestamp = global.__frameTimestamp || performance.now(); prepareAnimation(frameTimestamp, value, animations[key], oldValues[key]); animations[key] = value; hasAnimations = true; } }); function frame(timestamp) { const { animations, last, isAnimationCancelled } = state; if (isAnimationCancelled) { state.isAnimationRunning = false; return; } const updates = {}; let allFinished = true; Object.keys(animations).forEach(propName => { const finished = runAnimations(animations[propName], timestamp, propName, updates, animationsActive); if (finished) { last[propName] = updates[propName]; delete animations[propName]; } else { allFinished = false; } }); if (Object.keys(updates).length) { (0, _UpdateProps.updatePropsJestWrapper)(viewDescriptors, updates, maybeViewRef, animatedStyle, adapters); } if (!allFinished) { requestAnimationFrame(frame); } else { state.isAnimationRunning = false; } } if (hasAnimations) { state.animations = animations; if (!state.isAnimationRunning) { state.isAnimationCancelled = false; state.isAnimationRunning = true; frame(frameTimestamp); } } else { state.isAnimationCancelled = true; state.animations = []; } // calculate diff state.last = newValues; if (!(0, _utils.shallowEqual)(oldValues, newValues)) { (0, _UpdateProps.updatePropsJestWrapper)(viewDescriptors, newValues, maybeViewRef, animatedStyle, adapters); } } // check for invalid usage of shared values in returned object function checkSharedValueUsage(prop, currentKey) { if (Array.isArray(prop)) { // if it's an array (i.ex. transform) validate all its elements for (const element of prop) { checkSharedValueUsage(element, currentKey); } } else if (typeof prop === 'object' && prop.value === undefined) { // if it's a nested object, run validation for all its props for (const key of Object.keys(prop)) { checkSharedValueUsage(prop[key], key); } } else if (currentKey !== undefined && typeof prop === 'object' && prop.value !== undefined) { // if shared value is passed insted of its value, throw an error throw new Error(`invalid value passed to \`${currentKey}\`, maybe you forgot to use \`.value\`?`); } } function useAnimatedStyle(updater, dependencies, adapters) { const viewsRef = (0, _ViewDescriptorsSet.makeViewsRefSet)(); const initRef = (0, _react.useRef)(); let inputs = Object.values(updater._closure ?? {}); if ((0, _PlatformChecker.shouldBeUseWeb)()) { var _dependencies; if (!inputs.length && (_dependencies = dependencies) !== null && _dependencies !== void 0 && _dependencies.length) { // let web work without a Babel/SWC plugin inputs = dependencies; } if (__DEV__ && !inputs.length && !dependencies && !updater.__workletHash) { throw new Error(`useAnimatedStyle was used without a dependency array or Babel plugin. Please explicitly pass a dependency array, or enable the Babel/SWC plugin. For more, see the docs: https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/web-support#web-without-a-babel-plugin`); } } const adaptersArray = adapters ? Array.isArray(adapters) ? adapters : [adapters] : []; const adaptersHash = adapters ? (0, _utils.buildWorkletsHash)(adaptersArray) : null; const animationsActive = (0, _useSharedValue.useSharedValue)(true); const animatedStyle = (0, _react.useRef)({}); // build dependencies if (!dependencies) { dependencies = [...inputs, updater.__workletHash]; } else { dependencies.push(updater.__workletHash); } adaptersHash && dependencies.push(adaptersHash); if (!initRef.current) { const initialStyle = (0, _animation.initialUpdaterRun)(updater); (0, _utils.validateAnimatedStyles)(initialStyle); initRef.current = { initial: { value: initialStyle, updater: updater }, remoteState: (0, _core.makeRemote)({ last: initialStyle, animations: {}, isAnimationCancelled: false, isAnimationRunning: false }), viewDescriptors: (0, _ViewDescriptorsSet.makeViewDescriptorsSet)() }; } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const { initial, remoteState, viewDescriptors } = initRef.current; const sharableViewDescriptors = viewDescriptors.sharableViewDescriptors; const maybeViewRef = _NativeReanimated.default.native ? undefined : viewsRef; dependencies.push(sharableViewDescriptors); (0, _react.useEffect)(() => { let fun; let updaterFn = updater; if (adapters) { updaterFn = () => { 'worklet'; const newValues = updater(); adaptersArray.forEach(adapter => { adapter(newValues); }); return newValues; }; } if ((0, _PlatformChecker.isJest)()) { fun = () => { 'worklet'; jestStyleUpdater(sharableViewDescriptors, updater, remoteState, maybeViewRef, animationsActive, animatedStyle, adaptersArray); }; } else { fun = () => { 'worklet'; styleUpdater(sharableViewDescriptors, updaterFn, remoteState, maybeViewRef, animationsActive); }; } const mapperId = (0, _core.startMapper)(fun, inputs); return () => { (0, _core.stopMapper)(mapperId); }; }, dependencies); (0, _react.useEffect)(() => { animationsActive.value = true; return () => { animationsActive.value = false; }; }, []); checkSharedValueUsage(initial.value); if (process.env.JEST_WORKER_ID) { return { viewDescriptors, initial: initial, viewsRef, animatedStyle }; } else { return { viewDescriptors, initial: initial, viewsRef }; } } //# sourceMappingURL=useAnimatedStyle.js.map