UNPKG

rc-queue-anim

Version:
488 lines (409 loc) 16.2 kB
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray"; import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2"; import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties"; var _excluded = ["component", "componentProps", "interval", "duration", "delay", "type", "animConfig", "ease", "leaveReverse", "forcedReplay", "animatingClassName", "onEnd", "appear"]; import { useRef, useMemo, useLayoutEffect, useEffect, useState, createElement, cloneElement, forwardRef } from 'react'; import { findDOMNode } from 'react-dom'; import TweenOne, { Ticker } from 'tween-one'; import { toArrayChildren, findChildInChildrenByKey, windowIsUndefined, mergeChildren, transformArguments } from './utils'; import AnimTypes from './animTypes'; var noop = function noop() {}; export default /*#__PURE__*/forwardRef(function (props, ref) { var _props$component = props.component, component = _props$component === void 0 ? 'div' : _props$component, _props$componentProps = props.componentProps, componentProps = _props$componentProps === void 0 ? {} : _props$componentProps, _props$interval = props.interval, interval = _props$interval === void 0 ? 100 : _props$interval, _props$duration = props.duration, duration = _props$duration === void 0 ? 450 : _props$duration, _props$delay = props.delay, delay = _props$delay === void 0 ? 0 : _props$delay, _props$type = props.type, type = _props$type === void 0 ? 'right' : _props$type, _props$animConfig = props.animConfig, animConfig = _props$animConfig === void 0 ? null : _props$animConfig, _props$ease = props.ease, ease = _props$ease === void 0 ? 'easeOutQuart' : _props$ease, _props$leaveReverse = props.leaveReverse, leaveReverse = _props$leaveReverse === void 0 ? false : _props$leaveReverse, _props$forcedReplay = props.forcedReplay, forcedReplay = _props$forcedReplay === void 0 ? false : _props$forcedReplay, _props$animatingClass = props.animatingClassName, animatingClassName = _props$animatingClass === void 0 ? ['queue-anim-entering', 'queue-anim-leaving'] : _props$animatingClass, _props$onEnd = props.onEnd, onEnd = _props$onEnd === void 0 ? noop : _props$onEnd, _props$appear = props.appear, appear = _props$appear === void 0 ? true : _props$appear, tagProps = _objectWithoutProperties(props, _excluded); /** * @param childrenShow; * 记录 animation 里是否需要 startAnim; * 当前元素是否处在显示状态 * enterBegin 到 leaveComplete 之前都处于显示状态 */ var childrenShow = useRef({}); /** * @param keysToEnter; * 记录进场的 key; */ var keysToEnter = useRef([]); var recordKeysToEnter = useRef([]); /** * @param keysToLeave; * 记录出场的 key; */ var keysToLeave = useRef([]); var recordKeysToLeave = useRef([]); /** * @param placeholderTimeoutIds; * 进场时 deley 的 timeout 记录; */ var placeholderTimeoutIds = useRef({}); /** * @param childRefs; * 储存 children 的 ref; */ var childRefs = useRef({}); /** * @param recordAnimKeys; * 记录启动动画 key */ var recordAnimKeys = useRef({}); /** * @param recordAnimKeys; * 记录启动动画 key */ var recordTweenKeys = useRef({}); /** * @param oneEnterBool * 记录第一次进入 */ var oneEnterBool = useRef(false); var originalChildren = useRef([]); var _useState = useState(), _useState2 = _slicedToArray(_useState, 2), child = _useState2[0], setChild = _useState2[1]; var _useState3 = useState({}), _useState4 = _slicedToArray(_useState3, 2), childShow = _useState4[0], setChildShow = _useState4[1]; var getTweenSingleConfig = function getTweenSingleConfig(data, num, enterOrLeave) { var obj = {}; Object.keys(data).forEach(function (key) { if (Array.isArray(data[key])) { obj[key] = data[key][num]; } else if (!enterOrLeave && !num || enterOrLeave && num) { obj[key] = data[key]; } }); return obj; }; var getTweenAnimConfig = function getTweenAnimConfig(data, num, enterOrLeave) { if (Array.isArray(data)) { return data.map(function (item) { return getTweenSingleConfig(item, num, enterOrLeave); }); } return getTweenSingleConfig(data, num, enterOrLeave); }; var getTweenType = function getTweenType($type, num) { var data = AnimTypes[$type]; return getTweenAnimConfig(data, num); }; var getAnimData = function getAnimData(key, i, enterOrLeave, startOrEnd) { return ( /** * transformArguments 第一个为 enter, 第二个为 leave; * getTweenAnimConfig or getTweenType 第一个为到达的位置, 第二个为开始的位置。 * 用 tween-one 的数组来实现老的动画逻辑。。。 */ animConfig ? getTweenAnimConfig(transformArguments(animConfig, key, i)[enterOrLeave], startOrEnd, enterOrLeave) : getTweenType(transformArguments(type, key, i)[enterOrLeave], startOrEnd) ); }; var getTweenData = function getTweenData(key, i, $type) { var enterOrLeave = $type === 'enter' ? 0 : 1; var start = $type === 'enter' ? 1 : 0; var end = $type === 'enter' ? 0 : 1; var animate = getAnimData(key, i, enterOrLeave, end); var startAnim = $type === 'enter' && (forcedReplay || !childrenShow.current[key]) ? getAnimData(key, i, enterOrLeave, start) : null; var $ease = transformArguments(ease, key, i)[enterOrLeave]; var $duration = transformArguments(duration, key, i)[enterOrLeave]; if (Array.isArray(ease) && (ease.length > 2 || Array.isArray(ease[0]))) { $ease = $ease.map(function (num) { return num * 100; }); $ease = "M0,100C".concat($ease[0], ",").concat(100 - $ease[1], ",").concat($ease[2], ",").concat(100 - $ease[3], ",100,0"); } return { startAnim: startAnim, animate: animate, ease: $ease, duration: $duration }; }; var enterBegin = function enterBegin(key, e) { var elem = e.targets; elem.className = elem.className.replace(animatingClassName[1], ''); if (elem.className.indexOf(animatingClassName[0]) === -1) { elem.className = "".concat(elem.className, " ").concat(animatingClassName[0]).trim(); } if (keysToEnter.current.indexOf(key) >= 0) { keysToEnter.current.splice(keysToEnter.current.indexOf(key), 1); } childrenShow.current[key] = true; }; var enterComplete = function enterComplete(key, e) { if (keysToLeave.current.indexOf(key) >= 0) { return; } var elem = e.targets; elem.className = elem.className.replace(animatingClassName[0], '').trim(); delete recordTweenKeys.current[key]; onEnd({ key: key, type: 'enter', target: elem }); }; var leaveBegin = function leaveBegin(key, e) { var elem = e.targets; elem.className = elem.className.replace(animatingClassName[0], ''); if (elem.className.indexOf(animatingClassName[1]) === -1) { elem.className = "".concat(elem.className, " ").concat(animatingClassName[1]).trim(); } }; var leaveComplete = function leaveComplete(key, e) { // 切换时同时触发 onComplete。 手动跳出。。。 toArrayChildren(props.children).findIndex(function (c) { return c && c.key === key; }); if (toArrayChildren(props.children).findIndex(function (c) { return c && c.key === key; }) >= 0) { return; } delete childrenShow.current[key]; delete recordTweenKeys.current[key]; originalChildren.current = originalChildren.current.filter(function (c) { return c.key !== key; }); // 这里不用启动动画,,直接删; if (keysToLeave.current.indexOf(key) >= 0) { keysToLeave.current.splice(keysToLeave.current.indexOf(key), 1); } var needLeave = keysToLeave.current.some(function (c) { return childShow[c]; }); if (!needLeave) { var currentChildren = toArrayChildren(props.children); setChild(currentChildren); setChildShow(_objectSpread({}, childrenShow.current)); recordKeysToLeave.current.forEach(function (k) { delete recordAnimKeys.current[k]; }); } var elem = e.targets; elem.className = elem.className.replace(animatingClassName[1], '').trim(); onEnd({ key: key, type: 'leave', target: elem }); }; var performEnterBegin = function performEnterBegin(key) { childShow[key] = true; Ticker.clear(placeholderTimeoutIds.current[key]); delete placeholderTimeoutIds.current[key]; setChildShow(_objectSpread({}, childShow)); }; var performEnter = function performEnter(key, i) { var $interval = transformArguments(interval, key, i)[0]; var $delay = transformArguments(delay, key, i)[0]; placeholderTimeoutIds.current[key] = Ticker.timeout(function () { performEnterBegin(key); }, $interval * i + $delay); }; var performLeave = function performLeave(key) { Ticker.clear(placeholderTimeoutIds.current[key]); delete placeholderTimeoutIds.current[key]; }; var getTweenOneEnterOrLeave = function getTweenOneEnterOrLeave(key, i, $delay, $type) { var animateData = getTweenData(key, i, $type); var onStart = function onStart(e) { ($type === 'enter' ? enterBegin : leaveBegin)(key, e); }; var onComplete = function onComplete(e) { ($type === 'enter' ? enterComplete : leaveComplete)(key, e); }; if (Array.isArray(animateData.animate)) { var length = animateData.animate.length - 1; var animation = animateData.animate.map(function (item, ii) { return _objectSpread(_objectSpread({}, item), {}, { startAt: animateData.startAnim ? animateData.startAnim[ii] : undefined, duration: animateData.duration / length, delay: !ii && $type === 'leave' ? $delay : 0, onStart: !ii ? onStart : undefined, onComplete: ii === length ? onComplete : undefined }); }); return animation; } return _objectSpread(_objectSpread({}, animateData.animate), {}, { startAt: animateData.startAnim || undefined, ease: animateData.ease, duration: animateData.duration, onStart: onStart, onComplete: onComplete, delay: $delay }); }; useEffect(function () { return function () { Object.keys(recordTweenKeys.current).forEach(function (key) { var tween = recordTweenKeys.current[key]; if (!tween) { return; } tween.kill(); }); }; }, []); useEffect(function () { var nextChildren = toArrayChildren(props.children).filter(function (c) { return c; }); var currentChildren = originalChildren.current.filter(function (item) { return item; }); var newChildren = mergeChildren(currentChildren, nextChildren); var $keysToEnter = []; var $keysToLeave = []; if (!appear && !oneEnterBool.current) { var $childShow = {}; newChildren.forEach(function (c) { if (!c || !c.key) { return; } $childShow[c.key] = true; }); originalChildren.current = newChildren; childrenShow.current = _objectSpread({}, $childShow); setChildShow($childShow); } else { // console.log(nextChildren, recordAnimKeys.current, keysToEnter.current, keysToLeave.current); currentChildren.forEach(function (c) { if (!c) { return; } var key = c.key; var hasNext = findChildInChildrenByKey(nextChildren, key); if (!hasNext && key) { $keysToLeave.push(key); Ticker.clear(placeholderTimeoutIds.current[key]); delete placeholderTimeoutIds.current[key]; } }); nextChildren.forEach(function (c) { if (!c) { return; } var key = c.key; var hasPrev = findChildInChildrenByKey(currentChildren, key); // 如果 nextChildren 和当前的一致,且动画里是出场,改回进场; if (!hasPrev && key || (!recordAnimKeys.current[key] || recordAnimKeys.current[key] === 'leave' || keysToEnter.current.indexOf(key) >= 0) && $keysToLeave.indexOf(key) === -1) { $keysToEnter.push(key); } }); } // console.log('child update', $keysToEnter, $keysToLeave, newChildren); keysToEnter.current = $keysToEnter; // keysToEnter 在启动时就会删除; recordKeysToEnter.current = [].concat($keysToEnter); keysToLeave.current = $keysToLeave; recordKeysToLeave.current = [].concat($keysToLeave); // console.log($keysToEnter, $keysToLeave); setChild(newChildren); }, [props.children]); useLayoutEffect(function () { originalChildren.current = child || []; if (appear || oneEnterBool.current) { var $keysToEnter = _toConsumableArray(keysToEnter.current); var $keysToLeave = _toConsumableArray(keysToLeave.current); $keysToEnter.forEach(performEnter); $keysToLeave.forEach(performLeave); } if (child) { oneEnterBool.current = true; } }, [child]); useLayoutEffect(function () { if (child) { child.forEach(function (item) { var key = item.key; var dom = childRefs.current[key]; if (!dom) { return; } var animation; var index = keysToLeave.current.indexOf(key); // children.findIndex(c => c.key === key); var $interval = transformArguments(interval, key, index); var $delay = transformArguments(delay, key, index); // 处理出场 if (index >= 0) { if (recordAnimKeys.current[key] === 'leave') { return; } var order = leaveReverse ? keysToLeave.current.length - index - 1 : index; var d = $interval[1] * order + $delay[1]; animation = getTweenOneEnterOrLeave(key, index, d, 'leave'); recordAnimKeys.current[key] = 'leave'; } else { if (recordAnimKeys.current[key] === 'enter' || keysToEnter.current.indexOf(key) === -1) { return; } index = recordKeysToEnter.current.indexOf(key); var _d = $interval[0] * index + $delay[0]; // console.log(recordAnimKeys.current[key], dom); animation = getTweenOneEnterOrLeave(key, index, recordAnimKeys.current[key] === 'leave' ? _d : 0, 'enter'); recordAnimKeys.current[key] = 'enter'; } if (recordTweenKeys.current[key]) { recordTweenKeys.current[key].kill(); } if (forcedReplay) { var anim = _objectSpread(_objectSpread({}, Array.isArray(animation) ? animation[0].startAt : animation.startAt), {}, { type: 'set' }); TweenOne(dom, { animation: anim }); } recordTweenKeys.current[key] = TweenOne(dom, { animation: animation }); }); } }, [childShow, child]); return useMemo(function () { // console.log('--------render--------', childShow); if (windowIsUndefined) { return /*#__PURE__*/createElement(component, _objectSpread(_objectSpread(_objectSpread({}, tagProps), componentProps), {}, { ref: ref })); } var childrenToRender = toArrayChildren(child).map(function (item) { if (!item || !item.key) { return item; } return childShow[item.key] && /*#__PURE__*/cloneElement(item, { ref: function ref(c) { childRefs.current[item.key] = c instanceof Element ? c : findDOMNode(c); if (!c) { delete childRefs.current[item.key]; } }, key: item.key }); }); var p = _objectSpread(_objectSpread(_objectSpread({}, tagProps), componentProps), {}, { ref: ref }); return /*#__PURE__*/createElement(component, p, childrenToRender); }, [childShow, child]); });