react-atv
Version:
A port of @drewwilson’s atvImg (Apple TV 3D parallax effect) library in React
197 lines (162 loc) • 7.92 kB
JavaScript
'use strict';
exports.__esModule = true;
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _styles = require('../styles');
var _styles2 = _interopRequireDefault(_styles);
var AtvImg = (function (_Component) {
_inherits(AtvImg, _Component);
function AtvImg() {
var _this = this;
_classCallCheck(this, AtvImg);
_Component.apply(this, arguments);
this.state = {
rootElemWidth: 0,
rootElemHeight: 0,
isOnHover: false,
container: {},
shine: {},
children: []
};
this.handleMove = function (_ref) {
var pageX = _ref.pageX;
var pageY = _ref.pageY;
var childrenCount = _this.props.children.length; // the number of children
var _state = _this.state;
var rootElemWidth = _state.rootElemWidth;
var rootElemHeight = _state.rootElemHeight;
var _document$body = document.body;
var bodyScrollTop = _document$body.scrollTop;
var bodyScrollLeft = _document$body.scrollLeft;
var offsets = _this.refs.root.getBoundingClientRect();
var wMultiple = 320 / rootElemWidth;
var offsetX = 0.52 - (pageX - offsets.left - bodyScrollLeft) / rootElemWidth; // cursor position X
var offsetY = 0.52 - (pageY - offsets.top - bodyScrollTop) / rootElemHeight; // cursor position Y
var dy = pageY - offsets.top - bodyScrollTop - rootElemHeight / 2; // center Y of container
var dx = pageX - offsets.left - bodyScrollLeft - rootElemWidth / 2; // center X of container
var yRotate = (offsetX - dx) * (0.07 * wMultiple); // rotation for container Y
var xRotate = (dy - offsetY) * (0.1 * wMultiple); // rotation for container X
var arad = Math.atan2(dy, dx); // angle between cursor and center of container in RAD
var rawAngle = arad * 180 / Math.PI - 90; // convert rad to degrees
var angle = rawAngle < 0 ? rawAngle + 360 : rawAngle;
_this.setState({
container: {
transform: 'rotateX(' + xRotate + 'deg) rotateY(' + yRotate + 'deg)' + (_this.state.isOnHover ? ' scale3d(1.07,1.07,1.07)' : '')
},
shine: {
background: 'linear-gradient(' + angle + 'deg, rgba(255, 255, 255, ' + (pageY - offsets.top - bodyScrollTop) / rootElemHeight * 0.4 + ') 0%, rgba(255, 255, 255, 0) 80%)'
},
children: _this.props.children.map(function (_, idx) {
if (!_.props.className.match(/atv-fixed/)) {
return {
transform: 'translateX(' + offsetX * (childrenCount - idx) * (idx * 2.5 / wMultiple) + 'px) translateY(' + offsetY * childrenCount * (idx * 2.5 / wMultiple) + 'px)'
};
}
})
});
};
this.handleTouchMove = function (evt) {
evt.preventDefault();
var _evt$touches$0 = evt.touches[0];
var pageX = _evt$touches$0.pageX;
var pageY = _evt$touches$0.pageY;
_this.handleMove({ pageX: pageX, pageY: pageY });
};
this.handleEnter = function () {
_this.setState({ isOnHover: true });
};
this.handleLeave = function () {
_this.setState({
isOnHover: false,
container: {},
shine: {},
children: []
});
};
this.renderShadow = function () {
return _react2['default'].createElement('div', { style: _extends({}, _styles2['default'].shadow, _this.state.isOnHover ? _styles2['default'].shadowOnHover : {}) });
};
this.renderLayers = function () {
return _react2['default'].createElement(
'div',
{ style: _styles2['default'].children },
_this.props.children && _this.props.children.map(function (element, idx) {
return _react2['default'].createElement(
'div',
{
style: _extends({}, _styles2['default'].renderedLayer, _this.state.children[idx] ? _this.state.children[idx] : {}),
key: idx
},
element
);
})
);
};
this.renderShine = function () {
return _react2['default'].createElement('div', { style: _extends({}, _styles2['default'].shine, _this.state.shine) });
};
}
AtvImg.prototype.componentDidMount = function componentDidMount() {
if (!this.props.isStatic) {
this.setState({ // eslint-disable-line react/no-did-mount-set-state
// this is a legit use case. we must trigger a re-render. don't worry.
rootElemWidth: this.refs.root.clientWidth || this.refs.root.offsetWidth || this.refs.root.scrollWidth,
rootElemHeight: this.refs.root.clientHeight || this.refs.root.offsetHeight || this.refs.root.scrollHeight
});
}
};
AtvImg.prototype.render = function render() {
if (this.props.isStatic) {
return _react2['default'].createElement(
'div',
{
style: _extends({}, _styles2['default'].root, this.props.style ? this.props.style : {}),
className: this.props.className || ''
},
_react2['default'].createElement('img', { style: _styles2['default'].staticFallback, src: this.props.staticFallback })
);
}
return _react2['default'].createElement(
'div',
{
style: _extends({}, _styles2['default'].root, {
transform: 'perspective(' + this.state.rootElemWidth * 3 + 'px)'
}, this.props.style ? this.props.style : {}),
onMouseMove: this.handleMove,
onMouseEnter: this.handleEnter,
onMouseLeave: this.handleLeave,
onTouchMove: this.handleTouchMove,
onTouchStart: this.handleEnter,
onTouchEnd: this.handleLeave,
className: this.props.className || '',
ref: 'root'
},
_react2['default'].createElement(
'div',
{ style: _extends({}, _styles2['default'].container, this.state.container) },
this.renderShadow(),
this.renderLayers(),
this.renderShine()
)
);
};
_createClass(AtvImg, null, [{
key: 'propTypes',
value: {
children: _react.PropTypes.node.isRequired,
isStatic: _react.PropTypes.bool,
staticFallback: _react.PropTypes.string,
className: _react.PropTypes.string,
style: _react.PropTypes.object
},
enumerable: true
}]);
return AtvImg;
})(_react.Component);
exports['default'] = AtvImg;
module.exports = exports['default'];