UNPKG

react-simple-animate

Version:
422 lines (398 loc) 17.5 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var React = require('react'); function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n["default"] = e; return Object.freeze(n); } var React__namespace = /*#__PURE__*/_interopNamespace(React); const DEFAULT_DURATION = 0.3; const DEFAULT_EASE_TYPE = 'linear'; const DEFAULT_DIRECTION = 'normal'; const DEFAULT_FILLMODE = 'none'; const RUNNING = 'running'; const PAUSED = 'paused'; const ALL = 'all'; var calculateTotalDuration = ({ duration = DEFAULT_DURATION, delay = 0, overlay = 0, }) => duration + delay - overlay || 0; var isUndefined = (val) => val === undefined; function getSequenceId(sequenceIndex, sequenceId, defaultValue) { if (isUndefined(sequenceId) && isUndefined(sequenceIndex)) { return defaultValue || 0; } if (sequenceIndex && sequenceIndex >= 0) { return sequenceIndex; } if (sequenceId) { return sequenceId; } return 0; } const AnimateContext = React__namespace.createContext({ animationStates: {}, register: () => { }, }); function AnimateGroup({ play, sequences = [], children, }) { const [animationStates, setAnimationStates] = React__namespace.useState({}); const animationsRef = React__namespace.useRef({}); const register = React__namespace.useCallback((data) => { const { sequenceIndex, sequenceId } = data; if (!isUndefined(sequenceId) || !isUndefined(sequenceIndex)) { animationsRef.current[getSequenceId(sequenceIndex, sequenceId)] = data; } }, []); React__namespace.useEffect(() => { const sequencesToAnimate = Array.isArray(sequences) && sequences.length ? sequences : Object.values(animationsRef.current); const localAnimationState = {}; (play ? sequencesToAnimate : [...sequencesToAnimate].reverse()).reduce((previous, { sequenceId, sequenceIndex, duration = DEFAULT_DURATION, delay, overlay, }, currentIndex) => { const id = getSequenceId(sequenceIndex, sequenceId, currentIndex); const currentTotalDuration = calculateTotalDuration({ duration, delay, overlay, }); const totalDuration = currentTotalDuration + previous; localAnimationState[id] = { play, pause: !play, delay: (delay || 0) + previous, controlled: true, }; return totalDuration; }, 0); setAnimationStates(localAnimationState); // eslint-disable-next-line react-hooks/exhaustive-deps }, [play]); return (React__namespace.createElement(AnimateContext.Provider, { value: { animationStates, register } }, children)); } var secToMs = (ms) => (ms || 0) * 1000; function Animate(props) { const { play, children, render, start, end, complete = '', onComplete, delay = 0, duration = DEFAULT_DURATION, easeType = DEFAULT_EASE_TYPE, sequenceId, sequenceIndex, } = props; const onCompleteTimeRef = React__namespace.useRef(); const [style, setStyle] = React__namespace.useState(start || {}); const { register, animationStates = {} } = React__namespace.useContext(AnimateContext); const id = getSequenceId(sequenceIndex, sequenceId); React__namespace.useEffect(() => { if ((!isUndefined(sequenceIndex) && sequenceIndex >= 0) || sequenceId) { register(props); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); React__namespace.useEffect(() => { const animationState = animationStates[id] || {}; setStyle({ ...(play || animationState.play ? end : start), transition: `${ALL} ${duration}s ${easeType} ${animationState.delay || delay}s`, }); if (play && (complete || onComplete)) { onCompleteTimeRef.current = setTimeout(() => { complete && setStyle(complete); onComplete && onComplete(); }, secToMs((animationState.delay || delay) + duration)); } return () => onCompleteTimeRef.current && clearTimeout(onCompleteTimeRef.current); }, [ id, animationStates, play, duration, easeType, delay, onComplete, start, end, complete, ]); return render ? render({ style }) : React__namespace.createElement("div", { style: style }, children); } var camelCaseToDash = (camelCase) => camelCase ? camelCase.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`) : ''; const generateKeyframes = (keyframes) => { const animationLength = keyframes.length; return keyframes.reduce((previous, keyframe, currentIndex) => { const keyframePercentage = parseFloat((100 / (animationLength - 1)).toFixed(2)) * currentIndex; if (typeof keyframe === 'string') { return `${previous} ${keyframePercentage}% {${keyframe}}`; } const keys = Object.keys(keyframe); if (keys.length && isNaN(+keys[0])) { const keyframeContent = keys.reduce((acc, key) => `${acc} ${camelCaseToDash(key)}: ${keyframe[key]};`, ''); return `${previous} ${keyframePercentage}% {${keyframeContent}}`; } return `${previous} ${keys[0]}% {${keyframe[keys[0]]}}`; }, ''); }; function createStyle({ keyframes, animationName, }) { return `@keyframes ${animationName} {${generateKeyframes(keyframes)}}`; } function createTag({ keyframes, animationName, }) { var _a, _b, _c, _d; let styleTag = document.querySelector('style[data-id=rsi]'); if (!styleTag) { styleTag = document.createElement('style'); styleTag.setAttribute('data-id', 'rsi'); document.head.appendChild(styleTag); } const index = (_c = (_b = (_a = styleTag.sheet) === null || _a === void 0 ? void 0 : _a.cssRules) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0; try { (_d = styleTag.sheet) === null || _d === void 0 ? void 0 : _d.insertRule(createStyle({ keyframes, animationName, }), index); } catch (e) { console.error('react simple animate, error found during insert style ', e); // eslint-disable-line no-console } return { styleTag, index, }; } var deleteRules = (sheet, deleteName) => { if (!sheet) { return; } const index = Object.values(sheet.cssRules).findIndex(({ name }) => name === deleteName); if (index >= 0) { sheet.deleteRule(index); } }; var createRandomName = () => `RSI-${Math.random() .toString(36) .substr(2, 9)}`; var getPlayState = (pause) => (pause ? PAUSED : RUNNING); function AnimateKeyframes(props) { const { children, play = false, pause = false, render, duration = DEFAULT_DURATION, delay = 0, easeType = DEFAULT_EASE_TYPE, direction = DEFAULT_DIRECTION, fillMode = DEFAULT_FILLMODE, iterationCount = 1, sequenceIndex, keyframes, sequenceId, } = props; let pauseValue; const animationNameRef = React__namespace.useRef({ forward: '', reverse: '', }); const controlled = React__namespace.useRef(false); const styleTagRef = React__namespace.useRef({ forward: null, reverse: null, }); const id = getSequenceId(sequenceIndex, sequenceId); const { register, animationStates = {} } = React__namespace.useContext(AnimateContext); const animateState = animationStates[id] || {}; const [, forceUpdate] = React__namespace.useState(false); React__namespace.useEffect(() => { const styleTag = styleTagRef.current; const animationName = animationNameRef.current; animationNameRef.current.forward = createRandomName(); let result = createTag({ animationName: animationNameRef.current.forward, keyframes, }); styleTagRef.current.forward = result.styleTag; animationNameRef.current.reverse = createRandomName(); result = createTag({ animationName: animationNameRef.current.reverse, keyframes: keyframes.reverse(), }); styleTagRef.current.reverse = result.styleTag; register(props); if (play) { forceUpdate(true); } return () => { var _a, _b; deleteRules((_a = styleTag.forward) === null || _a === void 0 ? void 0 : _a.sheet, animationName.forward); deleteRules((_b = styleTag.reverse) === null || _b === void 0 ? void 0 : _b.sheet, animationName.reverse); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); if (animateState.controlled && !controlled.current) { pauseValue = animateState.pause; if (!animateState.pause) { controlled.current = true; } } else { pauseValue = pause; } const style = { animation: `${duration}s ${easeType} ${animateState.delay || delay}s ${iterationCount} ${direction} ${fillMode} ${getPlayState(pauseValue)} ${((animateState.controlled ? animateState.play : play) ? animationNameRef.current.forward : animationNameRef.current.reverse) || ''}`, }; return render ? render({ style }) : React__namespace.createElement("div", { style: style || {} }, children); } function useAnimate(props) { const { start, end, complete, onComplete, delay = 0, duration = DEFAULT_DURATION, easeType = DEFAULT_EASE_TYPE, } = props; const transition = React__namespace.useMemo(() => `${ALL} ${duration}s ${easeType} ${delay}s`, [duration, easeType, delay]); const [animate, setAnimate] = React__namespace.useState({ isPlaying: false, style: { ...start, transition }, }); const { isPlaying, style } = animate; const onCompleteTimeRef = React__namespace.useRef(); React__namespace.useEffect(() => { if ((onComplete || complete) && isPlaying) { onCompleteTimeRef.current = setTimeout(() => { if (onComplete) { onComplete(); } if (complete) { setAnimate((animate) => ({ ...animate, style: complete, })); } }, secToMs(delay + duration)); } return () => onCompleteTimeRef.current && clearTimeout(onCompleteTimeRef.current); }, [animate, complete, delay, duration, isPlaying, onComplete]); return { isPlaying, style, play: React__namespace.useCallback((isPlaying) => { setAnimate((animate) => ({ ...animate, style: { ...(isPlaying ? end : start), transition, }, isPlaying, })); }, [end, start, transition]), }; } function useAnimateKeyframes(props) { const { duration = DEFAULT_DURATION, delay = 0, easeType = DEFAULT_EASE_TYPE, direction = DEFAULT_DIRECTION, fillMode = DEFAULT_FILLMODE, iterationCount = 1, keyframes, } = props; const animationNameRef = React__namespace.useRef({ forward: '', reverse: '', }); const styleTagRef = React__namespace.useRef({ forward: null, reverse: null, }); const { register } = React__namespace.useContext(AnimateContext); const [isPlaying, setIsPlaying] = React__namespace.useState(null); const [isPaused, setIsPaused] = React__namespace.useState(false); React__namespace.useEffect(() => { const styleTag = styleTagRef.current; const animationName = animationNameRef.current; animationNameRef.current.forward = createRandomName(); let result = createTag({ animationName: animationNameRef.current.forward, keyframes, }); styleTagRef.current.forward = result.styleTag; animationNameRef.current.reverse = createRandomName(); result = createTag({ animationName: animationNameRef.current.reverse, keyframes: keyframes.reverse(), }); styleTagRef.current.reverse = result.styleTag; register(props); return () => { var _a, _b; deleteRules((_a = styleTag.forward) === null || _a === void 0 ? void 0 : _a.sheet, animationName.forward); deleteRules((_b = styleTag.reverse) === null || _b === void 0 ? void 0 : _b.sheet, animationName.reverse); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const style = { animation: `${duration}s ${easeType} ${delay}s ${iterationCount} ${direction} ${fillMode} ${getPlayState(isPaused)} ${isPlaying === null ? '' : isPlaying ? animationNameRef.current.forward : animationNameRef.current.reverse}`, }; return { style, play: setIsPlaying, pause: setIsPaused, isPlaying: !!isPlaying, }; } function createArrayWithNumbers(length) { return Array.from({ length }, () => null); } function useAnimateGroup(props) { const { sequences = [] } = props; const defaultArray = createArrayWithNumbers(sequences.length).map((_, index) => props.sequences[index].start); const [styles, setStyles] = React__namespace.useState(defaultArray); const [isPlaying, setPlaying] = React__namespace.useState(false); const animationNamesRef = React__namespace.useRef([]); const styleTagRef = React__namespace.useRef([]); React__namespace.useEffect(() => { sequences.forEach(({ keyframes }, i) => { if (!Array.isArray(keyframes)) { return; } if (!animationNamesRef.current[i]) { animationNamesRef.current[i] = {}; styleTagRef.current[i] = {}; } animationNamesRef.current[i].forward = createRandomName(); let result = createTag({ animationName: animationNamesRef.current[i].forward, keyframes, }); styleTagRef.current[i].forward = result.styleTag; animationNamesRef.current[i].reverse = createRandomName(); result = createTag({ animationName: animationNamesRef.current[i].reverse, keyframes: keyframes.reverse(), }); styleTagRef.current[i].reverse = result.styleTag; }); const styleTags = styleTagRef.current; const animationNames = animationNamesRef.current; return () => Object.values(animationNames).forEach(({ forward, reverse }, i) => { var _a, _b; deleteRules((_a = styleTags[i].forward) === null || _a === void 0 ? void 0 : _a.sheet, forward); deleteRules((_b = styleTags[i].reverse) === null || _b === void 0 ? void 0 : _b.sheet, reverse); }); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const play = React__namespace.useCallback((isPlay) => { let totalDuration = 0; const animationRefWithOrder = isPlay ? animationNamesRef.current : [...animationNamesRef.current].reverse(); const styles = (isPlay ? sequences : [...sequences].reverse()).map((current, currentIndex) => { const { duration = DEFAULT_DURATION, delay = 0, overlay, keyframes, iterationCount = 1, easeType = DEFAULT_EASE_TYPE, direction = DEFAULT_DIRECTION, fillMode = DEFAULT_FILLMODE, end = {}, start = {}, } = current; const delayDuration = currentIndex === 0 ? delay : totalDuration; const transition = `${ALL} ${duration}s ${easeType} ${delayDuration}s`; totalDuration = calculateTotalDuration({ duration, delay, overlay }) + totalDuration; return keyframes ? { animation: `${duration}s ${easeType} ${delayDuration}s ${iterationCount} ${direction} ${fillMode} ${RUNNING} ${isPlay ? animationRefWithOrder[currentIndex].forward : animationRefWithOrder[currentIndex].reverse}`, } : { ...(isPlay ? end : start), transition, }; }); setStyles(isPlay ? styles : [...styles].reverse()); setPlaying(isPlay); }, []); return { styles, play, isPlaying }; } exports.Animate = Animate; exports.AnimateGroup = AnimateGroup; exports.AnimateKeyframes = AnimateKeyframes; exports.useAnimate = useAnimate; exports.useAnimateGroup = useAnimateGroup; exports.useAnimateKeyframes = useAnimateKeyframes;