yunjiwong-slider
Version:
banner-anim animation component for react
293 lines (273 loc) • 10.3 kB
JavaScript
import _extends from 'babel-runtime/helpers/extends';
import _typeof from 'babel-runtime/helpers/typeof';
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 BgElement from './BgElement';
import TweenOne from 'rc-tween-one';
import ticker from 'rc-tween-one/es/ticker';
import ease from 'tween-functions';
import { getGsapType, isConvert, stylesToCss, checkStyleName } from 'style-utils';
import { currentScrollTop, currentScrollLeft, dataToArray, toArrayChildren } from './utils';
function noop() {}
var Element = function (_Component) {
_inherits(Element, _Component);
function Element() {
_classCallCheck(this, Element);
var _this = _possibleConstructorReturn(this, (Element.__proto__ || Object.getPrototypeOf(Element)).apply(this, arguments));
_this.onMouseMove = function (e) {
_this.domRect = _this.dom.getBoundingClientRect();
_this.enterMouse = _this.enterMouse || { x: _this.domRect.width / 2, y: _this.domRect.height / 2 };
_this.domWH = {
w: _this.domRect.width,
h: _this.domRect.height
};
_this.offsetTop = _this.domRect.top + currentScrollTop();
_this.offsetLeft = _this.domRect.left + currentScrollLeft();
var mouseXY = {
x: e.pageX - _this.offsetLeft,
y: e.pageY - _this.offsetTop
};
_this.setTicker(_this.followParallax, mouseXY);
};
_this.setTicker = function (followParallax, mouseXY) {
var callback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : noop;
ticker.clear(_this.tickerId);
_this.tickerId = 'bannerElementTicker' + (Date.now() + Math.random());
var startFrame = ticker.frame;
var startX = _this.enterMouse.x;
var startY = _this.enterMouse.y;
var duration = followParallax.duration || 450;
var easeFunc = ease[followParallax.ease || 'easeOutQuad'];
var start = typeof followParallax.minMove === 'number' ? followParallax.minMove : 0.08;
ticker.wake(_this.tickerId, function () {
var moment = (ticker.frame - startFrame) * ticker.perFrame;
var ratio = easeFunc(moment, start, 1, duration);
_this.enterMouse.x = startX + (mouseXY.x - startX) * ratio;
_this.enterMouse.y = startY + (mouseXY.y - startY) * ratio;
_this.setFollowStyle(_this.domWH);
if (moment >= duration) {
ticker.clear(_this.tickerId);
callback();
}
});
};
_this.getFollowMouseMove = function () {
var onMouseMove = void 0;
if (_this.followParallax) {
if (_this.followParallax.delay) {
onMouseMove = !_this.delayTimeout ? null : _this.state.onMouseMove;
_this.delayTimeout = _this.delayTimeout || ticker.timeout(function () {
_this.setState({
onMouseMove: _this.onMouseMove
});
}, _this.followParallax.delay);
} else {
onMouseMove = _this.onMouseMove;
}
}
return onMouseMove;
};
_this.getFollowStyle = function (data, domWH) {
var style = {};
dataToArray(data.type).forEach(function (type) {
var mouseData = _this.enterMouse.x;
var domData = domWH.w;
var value = data.value;
if ((type.indexOf('y') >= 0 || type.indexOf('Y') >= 0) && type !== 'opacity') {
mouseData = _this.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) {
_this.doms.map(function (item, i) {
if (!item) {
return;
}
var data = _this.followParallax.data[i];
var style = _this.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(_this.props.children).map(function (item) {
if (item.type === BgElement) {
return React.cloneElement(item, { show: _this.state.show });
}
return item;
});
};
_this.reFollowParallax = function () {
_this.setTicker(_this.followParallax, {
x: _this.domRect.width / 2 - _this.offsetLeft,
y: _this.domRect.height / 2 - _this.offsetTop
}, function () {
_this.followParallax = null;
});
};
_this.animEnd = function () {
var type = _this.state.show ? 'enter' : 'leave';
_this.props.callBack(type);
_this.setState({ show: _this.props.show });
};
_this.animChildren = function (props, style, bgElem) {
if (_this.tickerId) {
ticker.clear(_this.tickerId);
}
if (_this.delayTimeout) {
ticker.clear(_this.delayTimeout);
_this.delayTimeout = null;
}
style.display = 'block';
props.component = _this.props.component;
_this.show = _this.state.show;
style.zIndex = _this.state.show ? 1 : 0;
props.children = _this.props.show && !_this.props.sync ? bgElem : _this.getChildren();
var childrenToRender = React.createElement(TweenOne, props);
var type = _this.state.show ? 'enter' : 'leave';
return _this.props.animType(childrenToRender, type, _this.props.direction, {
ease: _this.props.ease,
duration: _this.props.duration,
delay: _this.props.delay,
onComplete: _this.animEnd
}, _this.props.elemOffset, _this.props.leaveChildHide);
};
_this.state = {
show: _this.props.show
};
_this.tickerId = -1;
_this.enterMouse = null;
_this.delayTimeout = null;
_this.show = _this.state.show;
_this.followParallax = _this.props.followParallax;
_this.transform = checkStyleName('transform');
return _this;
}
_createClass(Element, [{
key: 'componentDidMount',
value: function componentDidMount() {
this.dom = ReactDOM.findDOMNode(this);
}
}, {
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(nextProps) {
var show = nextProps.show;
if (this.tickerId !== -1) {
ticker.clear(this.tickerId);
this.tickerId = -1;
}
var followParallax = nextProps.followParallax;
if (this.followParallax && !followParallax) {
this.reFollowParallax();
} else {
this.followParallax = followParallax;
}
this.setState({ show: show });
}
}, {
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 _this2 = this;
var props = _extends({}, this.props);
var style = _extends({}, props.style);
style.display = props.show ? 'block' : 'none';
style.position = 'absolute';
style.width = '100%';
props.style = style;
props.className = ('banner-anim-elem ' + (this.props.prefixCls || '')).trim();
var bgElem = toArrayChildren(this.props.children).filter(function (item) {
return item.type.isBannerAnimBgElement;
}).map(function (item) {
return React.cloneElement(item, { show: _this2.state.show });
});
['prefixCls', 'callBack', 'animType', 'duration', 'delay', 'ease', 'elemOffset', 'followParallax', 'show', 'type', 'direction', 'leaveChildHide', 'sync'].forEach(function (key) {
return delete props[key];
});
if (this.show === this.state.show) {
style[this.transform] = null;
if (!this.state.show) {
this.enterMouse = null;
return React.createElement(TweenOne, props, bgElem);
}
if (this.props.followParallax) {
props.onMouseMove = this.getFollowMouseMove();
}
return React.createElement(TweenOne, props, this.getChildren());
}
return this.animChildren(props, style, bgElem);
}
}]);
return Element;
}(Component);
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
};
Element.defaultProps = {
component: 'div',
callBack: noop
};
Element.BgElement = BgElement;
Element.isBannerAnimElement = true;
export default Element;