rc-banner-anim
Version:
banner-anim animation component for react
373 lines (343 loc) • 13 kB
JavaScript
import _extends from 'babel-runtime/helpers/extends';
import _objectWithoutProperties from 'babel-runtime/helpers/objectWithoutProperties';
import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
import _createClass from 'babel-runtime/helpers/createClass';
import _inherits from 'babel-runtime/helpers/inherits';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import { polyfill } from 'react-lifecycles-compat';
import TweenOne, { ticker } from 'rc-tween-one';
import easeTween from 'tween-functions';
import { getGsapType, isConvert, stylesToCss, checkStyleName } from 'style-utils';
import BgElement from './BgElement';
import { currentScrollTop, currentScrollLeft, dataToArray, toArrayChildren, setAnimCompToTagComp } from './utils';
import animType from './anim';
function noop() {}
var Element = function (_Component) {
_inherits(Element, _Component);
_createClass(Element, null, [{
key: 'getDerivedStateFromProps',
value: function getDerivedStateFromProps(props, _ref) {
var prevProps = _ref.prevProps,
$self = _ref.$self,
show = _ref.show,
mouseMoveType = _ref.mouseMoveType;
var nextState = {
prevProps: props
};
if (prevProps && props !== prevProps) {
if ($self.tickerId !== -1) {
ticker.clear($self.tickerId);
$self.tickerId = -1;
}
var followParallax = props.followParallax;
if ($self.followParallax && !followParallax) {
$self.reFollowParallax();
} else {
$self.followParallax = followParallax;
}
if (show || props.show || mouseMoveType) {
nextState.mouseMoveType = props.mouseMoveType;
}
}
return nextState;
}
}]);
function Element(props) {
_classCallCheck(this, Element);
var _this = _possibleConstructorReturn(this, (Element.__proto__ || Object.getPrototypeOf(Element)).call(this, props));
_initialiseProps.call(_this);
_this.state = {
show: props.show,
$self: _this
};
_this.tickerId = -1;
_this.enterMouse = null;
_this.delayTimeout = null;
_this.followParallax = props.followParallax;
_this.transform = checkStyleName('transform');
return _this;
}
_createClass(Element, [{
key: 'componentDidMount',
value: function componentDidMount() {
this.dom = ReactDOM.findDOMNode(this);
}
}, {
key: 'componentDidUpdate',
value: function componentDidUpdate() {
if (this.followParallax) {
this.doms = this.followParallax.data.map(function (item) {
return document.getElementById(item.id);
});
}
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
ticker.clear(this.timeoutID);
ticker.clear(this.delayTimeout);
this.delayTimeout = -1;
this.timeoutID = -1;
}
}, {
key: 'render',
value: function render() {
var _props = this.props,
prefixCls = _props.prefixCls,
callBack = _props.callBack,
propsAnimType = _props.animType,
duration = _props.duration,
delay = _props.delay,
ease = _props.ease,
elemOffset = _props.elemOffset,
followParallax = _props.followParallax,
show = _props.show,
type = _props.type,
direction = _props.direction,
leaveChildHide = _props.leaveChildHide,
sync = _props.sync,
ratio = _props.ratio,
mouseMoveType = _props.mouseMoveType,
children = _props.children,
propsStyle = _props.style,
componentProps = _props.componentProps,
props = _objectWithoutProperties(_props, ['prefixCls', 'callBack', 'animType', 'duration', 'delay', 'ease', 'elemOffset', 'followParallax', 'show', 'type', 'direction', 'leaveChildHide', 'sync', 'ratio', 'mouseMoveType', 'children', 'style', 'componentProps']);
var _state = this.state,
currentShow = _state.show,
currentMouseMoveType = _state.mouseMoveType;
var style = _extends({}, propsStyle);
style.display = show ? 'block' : 'none';
style.position = 'absolute';
style.width = '100%';
if (mouseMoveType !== 'end') {
style[this.transform] = '';
}
props.style = style;
props.className = ('banner-anim-elem ' + (prefixCls || '')).trim();
var bgElem = toArrayChildren(children).filter(function (item) {
return item && item.type.isBannerAnimBgElement;
}).map(function (item) {
return React.cloneElement(item, { show: props.show });
});
if (currentShow === show && !currentMouseMoveType || currentMouseMoveType === 'reChild') {
props.animation = { x: 0, y: 0, type: 'set' };
if (!show) {
this.enterMouse = null;
return React.createElement(TweenOne, props, bgElem);
}
if (followParallax) {
props.onMouseMove = this.getFollowMouseMove();
}
return React.createElement(TweenOne, props, mouseMoveType === 'update' ? bgElem : this.getChildren());
}
var $props = _extends({}, props, componentProps);
return this.animChildren($props, style, bgElem);
}
}]);
return Element;
}(Component);
var _initialiseProps = function _initialiseProps() {
var _this2 = this;
this.onMouseMove = function (e) {
_this2.domRect = _this2.dom.getBoundingClientRect();
_this2.enterMouse = _this2.enterMouse || { x: _this2.domRect.width / 2, y: _this2.domRect.height / 2 };
_this2.domWH = {
w: _this2.domRect.width,
h: _this2.domRect.height
};
_this2.offsetTop = _this2.domRect.top + currentScrollTop();
_this2.offsetLeft = _this2.domRect.left + currentScrollLeft();
var mouseXY = {
x: e.pageX - _this2.offsetLeft,
y: e.pageY - _this2.offsetTop
};
_this2.setTicker(_this2.followParallax, mouseXY);
};
this.setTicker = function (followParallax, mouseXY) {
var callback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : noop;
ticker.clear(_this2.tickerId);
_this2.tickerId = 'bannerElementTicker' + (Date.now() + Math.random());
var startFrame = ticker.frame;
var startX = _this2.enterMouse.x;
var startY = _this2.enterMouse.y;
var duration = followParallax.duration || 450;
var easeFunc = easeTween[followParallax.ease || 'easeOutQuad'];
var start = typeof followParallax.minMove === 'number' ? followParallax.minMove : 0.08;
ticker.wake(_this2.tickerId, function () {
var moment = (ticker.frame - startFrame) * ticker.perFrame;
var ratio = easeFunc(moment, start, 1, duration);
_this2.enterMouse.x = startX + (mouseXY.x - startX) * ratio;
_this2.enterMouse.y = startY + (mouseXY.y - startY) * ratio;
_this2.setFollowStyle(_this2.domWH);
if (moment >= duration) {
ticker.clear(_this2.tickerId);
callback();
}
});
};
this.getFollowMouseMove = function () {
var onMouseMove = void 0;
if (_this2.followParallax) {
if (_this2.followParallax.delay) {
onMouseMove = !_this2.delayTimeout ? null : _this2.state.onMouseMove;
_this2.delayTimeout = _this2.delayTimeout || ticker.timeout(function () {
_this2.setState({
onMouseMove: _this2.onMouseMove
});
}, _this2.followParallax.delay);
} else {
onMouseMove = _this2.onMouseMove;
}
}
return onMouseMove;
};
this.getFollowStyle = function (data, domWH) {
var style = {};
dataToArray(data.type).forEach(function (type) {
var mouseData = _this2.enterMouse.x;
var domData = domWH.w;
var value = data.value;
if ((type.indexOf('y') >= 0 || type.indexOf('Y') >= 0) && type !== 'opacity') {
mouseData = _this2.enterMouse.y;
domData = domWH.h;
}
var d = (mouseData - domData / 2) / (domData / 2) * value;
var _type = getGsapType(type);
var cssName = isConvert(_type);
if (cssName === 'transform') {
var transform = checkStyleName('transform');
style[transform] = style[transform] || {};
style[transform][_type] = stylesToCss(_type, d).trim();
} else if (cssName === 'filter') {
var filter = checkStyleName('filter');
style[filter] = style[filter] || {};
style[filter][_type] = stylesToCss(_type, d).trim();
} else {
style[cssName] = stylesToCss(_type, d).trim();
}
});
return style;
};
this.setFollowStyle = function (domWH) {
_this2.doms.forEach(function (item, i) {
if (!item) {
return;
}
var data = _this2.followParallax.data[i];
var style = _this2.getFollowStyle(data, domWH);
Object.keys(style).forEach(function (key) {
if (typeof style[key] === 'object') {
var styleStr = '';
Object.keys(style[key]).forEach(function (_key) {
styleStr += (' ' + _key + '(' + style[key][_key] + ')').trim();
});
item.style[key] = styleStr;
return;
}
item.style[key] = key.indexOf('backgroundPosition') >= 0 ? 'calc(' + (data.bgPosition || '0%') + ' + ' + style[key] + ' )' : style[key];
});
});
};
this.getChildren = function () {
return toArrayChildren(_this2.props.children).map(function (item, i) {
if (item && item.type === BgElement) {
return React.cloneElement(item, { show: _this2.state.show });
}
return _this2.useTagComp ? setAnimCompToTagComp(item, i) : item;
});
};
this.reFollowParallax = function () {
if (!_this2.domRect) {
return;
}
_this2.setTicker(_this2.followParallax, {
x: _this2.domRect.width / 2 - _this2.offsetLeft,
y: _this2.domRect.height / 2 - _this2.offsetTop
}, function () {
_this2.followParallax = null;
});
};
this.animEnd = function () {
var type = _this2.state.show ? 'enter' : 'leave';
_this2.props.callBack(type);
_this2.setState(function (_, props) {
return { show: props.show, mouseMoveType: null };
});
};
this.animChildren = function (props, style, bgElem) {
var _props2 = _this2.props,
elemOffset = _props2.elemOffset,
leaveChildHide = _props2.leaveChildHide,
ratio = _props2.ratio,
currentAnimType = _props2.animType,
direction = _props2.direction,
mouseMoveType = _props2.mouseMoveType,
ease = _props2.ease,
duration = _props2.duration,
delay = _props2.delay,
show = _props2.show,
sync = _props2.sync,
component = _props2.component;
if (_this2.tickerId) {
ticker.clear(_this2.tickerId);
}
if (_this2.delayTimeout) {
ticker.clear(_this2.delayTimeout);
_this2.delayTimeout = null;
}
style.display = 'block';
props.component = component;
style.zIndex = show ? 1 : 0;
var type = show ? 'enter' : 'leave';
_this2.useTagComp = (currentAnimType === animType.gridBar || currentAnimType === animType.grid) && (show === _this2.state.show || _this2.state.show && !show);
// 状态没改,锁定 children
props.children = !sync && (show && show !== _this2.state.show || !show && !_this2.state.show) ? bgElem : _this2.getChildren();
var childrenToRender = React.createElement(TweenOne, props);
var $ratio = mouseMoveType === 'end' && ratio <= 0.3 ? 1 - ratio : ratio;
var tag = currentAnimType(childrenToRender, type, direction, {
ease: ease,
duration: duration,
delay: delay,
onComplete: _this2.animEnd
}, elemOffset, leaveChildHide, $ratio, _this2.state.mouseMoveType === 'update');
var tagProps = _objectWithoutProperties(tag.props, []);
if (tagProps.animation) {
tagProps.moment = (tagProps.animation.duration + tagProps.animation.delay) * $ratio || 0;
tagProps.paused = _this2.state.mouseMoveType === 'update' || $ratio === 1;
}
return React.cloneElement(tag, tagProps);
};
};
Element.propTypes = {
children: PropTypes.any,
style: PropTypes.object,
prefixCls: PropTypes.string,
component: PropTypes.any,
elemOffset: PropTypes.object,
type: PropTypes.string,
animType: PropTypes.func,
ease: PropTypes.string,
duration: PropTypes.number,
delay: PropTypes.number,
direction: PropTypes.string,
callBack: PropTypes.func,
followParallax: PropTypes.any,
show: PropTypes.bool,
leaveChildHide: PropTypes.bool,
sync: PropTypes.bool,
ratio: PropTypes.number,
mouseMoveType: PropTypes.string,
componentProps: PropTypes.object
};
Element.defaultProps = {
component: 'div',
componentProps: {},
callBack: noop,
delay: 0
};
Element.BgElement = polyfill(BgElement);
Element.isBannerAnimElement = true;
export default polyfill(Element);