UNPKG

@atlaskit/motion

Version:

A set of utilities to apply motion in your application.

94 lines (92 loc) 4.11 kB
import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; /* eslint-disable @repo/internal/deprecations/deprecation-ticket-required */ import { useRef } from 'react'; import { easeInOut } from '../utils/curves'; import { durations } from '../utils/durations'; import { isReducedMotion } from '../utils/is-reduced-motion'; import { useElementRef } from '../utils/use-element-ref'; import { useLayoutEffect } from '../utils/use-layout-effect'; import { useRequestAnimationFrame } from '../utils/use-request-animation-frame'; import { useSetTimeout } from '../utils/use-set-timeout'; import { useSnapshotBeforeUpdate } from '../utils/use-snapshot-before-update'; /** * `useResizingHeight` animates height changes over state changes. If the height hasn't changed nothing will happen. * * __WARNING__: Potentially janky. This hook animates height which is * [notoriously unperformant](https://firefox-source-docs.mozilla.org/performance/bestpractices.html#Get_familiar_with_the_pipeline_that_gets_pixels_to_the_screen). * Test your app over low powered devices, you may want to avoid this if you can see it impacting FPS. * * See [examples](https://atlaskit.atlassian.com/packages/design-system/motion/docs/resizing-motions). * * @deprecated Use `useResizing` from `@atlaskit/motion/resizing` instead. Pass `dimension: 'height'` * to animate height changes. The new hook supports `'width'`, `'height'`, or `'both'`. */ export var useResizingHeight = function useResizingHeight() { var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, _ref$duration = _ref.duration, calcDuration = _ref$duration === void 0 ? function () { return durations.medium; } : _ref$duration, _ref$timingFunction = _ref.timingFunction, calcTimingFunction = _ref$timingFunction === void 0 ? function () { return easeInOut; } : _ref$timingFunction; var prevDimensions = useRef(); var _useElementRef = useElementRef(), _useElementRef2 = _slicedToArray(_useElementRef, 2), element = _useElementRef2[0], setElementRef = _useElementRef2[1]; // We cleanup on the next effect to prevent the previous timeout being called during // the next motion - as now the timeout has essentially been extended! var setTimeout = useSetTimeout({ cleanup: 'next-effect' }); var requestAnimationFrame = useRequestAnimationFrame(); useSnapshotBeforeUpdate(function () { if (isReducedMotion() || !element) { return; } prevDimensions.current = element.getBoundingClientRect(); }); useLayoutEffect(function () { if (isReducedMotion() || !element || !prevDimensions.current) { return; } // We might already be animating. // Because of that we need to expand to the destination height first. element.setAttribute('style', ''); var nextDimensions = element.getBoundingClientRect(); if (nextDimensions.height === prevDimensions.current.height) { return; } var duration = calcDuration(prevDimensions.current.height, nextDimensions.height); var newStyles = { height: "".concat(prevDimensions.current.height, "px"), willChange: 'height', transitionProperty: 'height', transitionDuration: "".concat(duration, "ms"), boxSizing: 'border-box', transitionTimingFunction: calcTimingFunction(prevDimensions.current.height, nextDimensions.height, duration) }; Object.assign(element.style, newStyles); // We split this over two animation frames so the DOM has enough time to flush the changes. // We are deliberately not skipping this frame if another render happens - if we do the motion doesn't finish properly. requestAnimationFrame(function () { requestAnimationFrame(function () { if (!element) { return; } element.style.height = "".concat(nextDimensions.height, "px"); setTimeout(function () { if (!element) { return; } element.setAttribute('style', ''); }, duration); }); }); }); return { ref: setElementRef }; };