UNPKG

rc-tween-one

Version:
345 lines (315 loc) 11.3 kB
import _extends from 'babel-runtime/helpers/extends'; import _classCallCheck from 'babel-runtime/helpers/classCallCheck'; import _createClass from 'babel-runtime/helpers/createClass'; import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn'; import _inherits from 'babel-runtime/helpers/inherits'; import React, { Component } from 'react'; import PropTypes from 'prop-types'; import ReactDom from 'react-dom'; import { dataToArray, objectEqual } from './util'; import Tween from './Tween'; import ticker from './ticker'; function noop() {} var perFrame = Math.round(1000 / 60); var objectOrArray = PropTypes.oneOfType([PropTypes.object, PropTypes.array]); var TweenOne = function (_Component) { _inherits(TweenOne, _Component); function TweenOne(props) { _classCallCheck(this, TweenOne); var _this = _possibleConstructorReturn(this, (TweenOne.__proto__ || Object.getPrototypeOf(TweenOne)).call(this, props)); _initialiseProps.call(_this); _this.rafID = -1; _this.setDefalut(props); _this.paused = props.paused; _this.reverse = props.reverse; _this.updateAnim = false; _this.forced = {}; _this.setForcedJudg(props); return _this; } _createClass(TweenOne, [{ key: 'componentDidMount', value: function componentDidMount() { this.dom = ReactDom.findDOMNode(this); if (this.dom && this.dom.nodeName !== '#text') { this.start(); } } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { if (!this.tween && !this.dom) { this.updateAnim = true; return; } // 动画处理 var newAnimation = nextProps.animation; var currentAnimation = this.props.animation; var equal = objectEqual(currentAnimation, newAnimation); if (!equal) { if (nextProps.resetStyle && this.tween) { this.tween.resetDefaultStyle(); } this.setDefalut(nextProps); this.updateAnim = true; } // 跳帧事件 moment; var nextMoment = nextProps.moment; if (typeof nextMoment === 'number' && nextMoment !== this.props.moment) { if (this.tween && !this.updateAnim) { this.startMoment = nextMoment; this.startFrame = ticker.frame; if (nextProps.paused) { this.raf(); } if (this.tween.progressTime >= this.tween.totalTime) { this.play(); } } else { this.setDefalut(nextProps); this.updateAnim = true; } } // 暂停倒放 if (this.paused !== nextProps.paused || this.reverse !== nextProps.reverse) { this.paused = nextProps.paused; this.reverse = nextProps.reverse; if (this.paused) { this.cancelRequestAnimationFrame(); } else if (this.reverse && nextProps.reverseDelay) { this.cancelRequestAnimationFrame(); ticker.timeout(this.restart, nextProps.reverseDelay); } else { // 在 form 状态下,暂停时拉 moment 时,start 有值恢复播放,在 delay 的时间没有处理。。 if (this.tween) { this.tween.resetAnimData(); this.tween.resetDefaultStyle(); } if (!this.updateAnim) { this.restart(); } } } var styleEqual = objectEqual(this.props.style, nextProps.style); if (!styleEqual) { // 在动画时更改了 style, 作为更改开始数值。 if (this.tween) { this.tween.reStart(nextProps.style); if (this.paused) { this.raf(); } } } this.setForcedJudg(nextProps); } }, { key: 'componentDidUpdate', value: function componentDidUpdate() { if (!this.dom) { this.dom = ReactDom.findDOMNode(this); } // 样式更新了后再执行动画; if (this.updateAnim && this.dom && this.dom.nodeName !== '#text') { if (this.tween) { this.cancelRequestAnimationFrame(); } this.start(); } } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { this.cancelRequestAnimationFrame(); } /** * @method setForcedJudg * @param props * QueueAnim 套在组件下面后导至子级变化。 * <QueueAnim component={Menu} > * <SubMenu key="a" title="导航"> * <Item /> * </SubMenu> * </QueueAnim> * rc-Menu 里是以 isXXX 来判断是 rc-Menu 的子级; * 如: 用 isSubMenu 来处理 hover 事件 * 地址: https://github.com/react-component/menu/blob/master/src/MenuMixin.js#L172 * 暂时方案: 在组件里添加判断用的值。 */ }, { key: 'render', value: function render() { var props = _extends({}, this.props); ['animation', 'component', 'componentProps', 'reverseDelay', 'attr', 'paused', 'reverse', 'repeat', 'yoyo', 'moment', 'resetStyle', 'forcedJudg'].forEach(function (key) { return delete props[key]; }); props.style = _extends({}, this.props.style); Object.keys(props.style).forEach(function (p) { if (p.match(/filter/i)) { ['Webkit', 'Moz', 'Ms', 'ms'].forEach(function (prefix) { props.style[prefix + 'Filter'] = props.style[p]; }); } }); // component 为空时调用子级的。。 if (!this.props.component) { if (!this.props.children) { return this.props.children; } var childrenProps = this.props.children.props; var style = childrenProps.style, className = childrenProps.className; // 合并 style 与 className。 var newStyle = _extends({}, style, props.style); var newClassName = props.className ? props.className + ' ' + className : className; return React.cloneElement(this.props.children, { style: newStyle, className: newClassName }); } return React.createElement(this.props.component, _extends({}, props, this.props.componentProps)); } }]); return TweenOne; }(Component); TweenOne.propTypes = { component: PropTypes.any, componentProps: PropTypes.any, animation: objectOrArray, children: PropTypes.any, style: PropTypes.object, paused: PropTypes.bool, reverse: PropTypes.bool, reverseDelay: PropTypes.number, yoyo: PropTypes.bool, repeat: PropTypes.number, moment: PropTypes.number, attr: PropTypes.string, onChange: PropTypes.func, resetStyle: PropTypes.bool, forcedJudg: PropTypes.object }; TweenOne.defaultProps = { component: 'div', componentProps: {}, reverseDelay: 0, repeat: 0, attr: 'style', onChange: noop }; var _initialiseProps = function _initialiseProps() { var _this2 = this; this.setForcedJudg = function (props) { Object.keys(_this2.forced).forEach(function (key) { delete _this2[key]; delete _this2.forced[key]; }); if (props.forcedJudg) { Object.keys(props.forcedJudg).forEach(function (key) { if (!_this2[key]) { _this2[key] = props.forcedJudg[key]; _this2.forced[key] = 1; } }); } }; this.setDefalut = function (props) { _this2.moment = props.moment || 0; _this2.startMoment = props.moment || 0; _this2.startFrame = ticker.frame; }; this.restart = function () { if (!_this2.tween) { return; } _this2.startMoment = _this2.moment; _this2.startFrame = ticker.frame; _this2.tween.reverse = _this2.reverse; _this2.tween.reverseStartTime = _this2.startMoment; _this2.raf(); _this2.play(); }; this.start = function () { _this2.updateAnim = false; var props = _this2.props; if (props.animation && Object.keys(props.animation).length) { _this2.tween = new Tween(_this2.dom, dataToArray(props.animation), { attr: props.attr }); _this2.tween.reverse = _this2.reverse; // 预先注册 raf, 初始动画数值。 _this2.raf(); // 开始动画 _this2.play(); } }; this.play = function () { _this2.cancelRequestAnimationFrame(); if (_this2.paused) { return; } _this2.rafID = ticker.add(_this2.raf); }; this.frame = function () { var yoyo = _this2.props.yoyo; var repeat = _this2.props.repeat; var totalTime = repeat === -1 ? Number.MAX_VALUE : _this2.tween.totalTime * (repeat + 1); repeat = repeat >= 0 ? repeat : Number.MAX_VALUE; var moment = (ticker.frame - _this2.startFrame) * perFrame + _this2.startMoment; if (_this2.reverse) { moment = (_this2.startMoment || 0) - (ticker.frame - _this2.startFrame) * perFrame; } moment = moment > totalTime ? totalTime : moment; moment = moment <= 0 ? 0 : moment; var repeatNum = Math.floor(moment / _this2.tween.totalTime) || 0; repeatNum = repeatNum > repeat ? repeat : repeatNum; var tweenMoment = moment - _this2.tween.totalTime * repeatNum; tweenMoment = tweenMoment < perFrame && !_this2.reverse ? 0 : tweenMoment; if (repeat && moment && moment - _this2.tween.totalTime * repeatNum < perFrame) { // 在重置样式之前补 complete; _this2.tween.frame(_this2.tween.totalTime * repeatNum); } if (moment < _this2.moment && !_this2.reverse || repeat !== 0 && repeatNum && tweenMoment <= perFrame) { // 在 form 状态下,暂停时拉 moment 时,start 有值,,往返方向播放时,在 delay 的时间没有处理。。 // 与上面的处理一样,删除 start ,重新走一遍 start。。 _this2.tween.resetAnimData(); _this2.tween.resetDefaultStyle(); } var yoyoReverse = yoyo && repeatNum % 2; if (yoyoReverse) { tweenMoment = _this2.tween.totalTime - tweenMoment; } _this2.tween.onChange = function (e) { var cb = _extends({}, e, { timelineMode: '' }); if (!moment && !_this2.reverse || _this2.reverse && _this2.moment === _this2.startMoment) { cb.timelineMode = 'onTimelineStart'; } else if (moment >= totalTime && !_this2.reverse || !moment && _this2.reverse) { cb.timelineMode = 'onTimelineComplete'; } else if (repeatNum !== _this2.timelineRepeatNum) { cb.timelineMode = 'onTimelineRepeat'; } else { cb.timelineMode = 'onTimelineUpdate'; } _this2.props.onChange(cb); }; _this2.moment = moment; _this2.timelineRepeatNum = repeatNum; _this2.tween.frame(tweenMoment); }; this.raf = function () { var tween = _this2.tween; _this2.frame(); if (tween !== _this2.tween) { // 在 onComplete 时更换动画时,raf 没结束,所以需要强制退出,避逸两个时间的冲突。 return null; } var repeat = _this2.props.repeat; var totalTime = repeat === -1 ? Number.MAX_VALUE : _this2.tween.totalTime * (repeat + 1); if (_this2.moment >= totalTime && !_this2.reverse || _this2.paused || _this2.reverse && _this2.moment === 0) { return _this2.cancelRequestAnimationFrame(); } return null; }; this.cancelRequestAnimationFrame = function () { ticker.clear(_this2.rafID); _this2.rafID = -1; }; }; TweenOne.isTweenOne = true; export default TweenOne;