Created
August 30, 2015 06:55
-
-
Save prenaux/5604df98e7855e960fb4 to your computer and use it in GitHub Desktop.
An updated MagicMove.jsx, faster than react-shuffle, but has issues with overlays and resizing, atm I'm using react-shuffle because of that.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var React = require('react'); | |
| var cloneWithProps = require('react/lib/cloneWithProps'); | |
| var Clones = React.createClass({ | |
| displayName: 'MagicMoveClones', | |
| propTypes: { | |
| children: React.PropTypes.node, | |
| }, | |
| childrenWithPositions: function childrenWithPositions() { | |
| var that = this; | |
| var children = []; | |
| React.Children.forEach(this.props.children, function (child) { | |
| var style = that.props.positions[child.key]; | |
| var key = child.key; | |
| children.push(cloneWithProps(child, { style: style, key: key })); | |
| }); | |
| return children.sort(function (a, b) { | |
| return (a.key < b.key) ? (-1) : ((a.key > b.key) ? 1 : 0); | |
| }); | |
| }, | |
| render: function render() { | |
| return React.createElement( | |
| 'div', | |
| { className: 'MagicMoveClones' }, | |
| this.childrenWithPositions() | |
| ); | |
| } | |
| }); | |
| var MagicMove = React.createClass({ | |
| displayName: 'MagicMove', | |
| propTypes: { | |
| positions: React.PropTypes.array, | |
| children: React.PropTypes.node, | |
| }, | |
| getInitialState: function getInitialState() { | |
| return { | |
| animating: false | |
| }; | |
| }, | |
| componentDidMount: function componentDidMount() { | |
| this.makePortal(); | |
| this.renderClonesInitially(); | |
| }, | |
| componentWillUnmount: function componentWillUnmount() { | |
| document.body.removeChild(this.portalNode); | |
| }, | |
| componentWillReceiveProps: function componentWillReceiveProps(nextProps) { | |
| this.startAnimation(nextProps); | |
| }, | |
| componentDidUpdate: function componentDidUpdate(prevProps) { | |
| if (this.state.animating) { | |
| this.renderClonesToNewPositions(prevProps); | |
| } | |
| }, | |
| makePortal: function makePortal() { | |
| this.portalNode = document.createElement('div'); | |
| this.portalNode.style.left = '-9999px'; | |
| document.body.appendChild(this.portalNode); | |
| }, | |
| addTransitionEndEvent: function addTransitionEndEvent() { | |
| // if you click RIGHT before the transition is done, the animation jumps, | |
| // its because the transitionend event fires even though its not quite | |
| // done, not sure how to hack around it yet. | |
| this._transitionHandler = callOnNthCall(this.props.children.length, this.finishAnimation); | |
| this.portalNode.addEventListener('transitionend', this._transitionHandler); | |
| }, | |
| removeTransitionEndEvent: function removeTransitionEndEvent() { | |
| this.portalNode.removeEventListener('transitionend', this._transitionHandler); | |
| }, | |
| startAnimation: function startAnimation(nextProps) { | |
| var that = this; | |
| if (this.state.animating) { | |
| return; | |
| } | |
| this.addTransitionEndEvent(); | |
| nextProps.animating = true; | |
| nextProps.positions = this.getPositions(); | |
| this.renderClones(nextProps, function () { | |
| that.setState({ animating: true }); | |
| }); | |
| }, | |
| renderClonesToNewPositions: function renderClonesToNewPositions(prevProps) { | |
| prevProps.positions = this.getPositions(); | |
| this.renderClones(prevProps); | |
| }, | |
| finishAnimation: function finishAnimation() { | |
| this.removeTransitionEndEvent(); | |
| this.portalNode.style.position = 'absolute'; | |
| this.portalNode.style.top = '0'; // PIERRE: required so that an unneeded scrollbar isn't created | |
| this.setState({ animating: false }); | |
| }, | |
| getPositions: function getPositions() { | |
| var that = this; | |
| var positions = {}; | |
| React.Children.forEach(this.props.children, function (child) { | |
| var ref = child.key; | |
| var node = that.refs[ref].getDOMNode(); | |
| var rect = node.getBoundingClientRect(); | |
| var computedStyle = getComputedStyle(node); | |
| var marginTop = parseInt(computedStyle.marginTop, 10); | |
| var marginLeft = parseInt(computedStyle.marginLeft, 10); | |
| var position = { | |
| top: (rect.top - marginTop) + window.scrollY, | |
| left: (rect.left - marginLeft) + window.scrollX, | |
| width: rect.width, | |
| height: rect.height, | |
| position: 'absolute' | |
| }; | |
| positions[ref] = position; | |
| }); | |
| return positions; | |
| }, | |
| renderClonesInitially: function renderClonesInitially() { | |
| this.props.positions = this.getPositions(); | |
| React.render(React.createElement(Clones, this.props), this.portalNode); | |
| }, | |
| renderClones: function renderClones(props, cb) { | |
| this.portalNode.style.position = ''; | |
| React.render(React.createElement(Clones, props), this.portalNode, cb); | |
| }, | |
| childrenWithRefs: function childrenWithRefs() { | |
| return React.Children.map(this.props.children, function (child) { | |
| return cloneWithProps(child, { ref: child.key }); | |
| }); | |
| }, | |
| render: function render() { | |
| var style = { | |
| opacity: this.state.animating ? 0 : 1, | |
| }; | |
| return React.createElement( | |
| 'div', | |
| { style: style }, | |
| this.childrenWithRefs() | |
| ); | |
| } | |
| }); | |
| function callOnNthCall(n, fn) { | |
| var calls = 0; | |
| return function () { | |
| calls++; | |
| if (calls === n) { | |
| calls = 0; | |
| return fn.apply(this, arguments); | |
| } | |
| }; | |
| } | |
| module.exports = MagicMove; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment