Created
December 24, 2015 18:37
-
-
Save miguelludert/ad456cb6a290601989aa to your computer and use it in GitHub Desktop.
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 ReactDOM = require("react-dom"); | |
var _ = require('lodash'); | |
/* | |
This component is meant to facilitate side to side slide transitions. | |
It's props are: | |
goToIndex : the index of the slide to show, REQUIRED | |
duration : the duration of the sliding, REQUIRED | |
easing : the css easing type, also known as the transition-timing-fuction, DEFAULTS to 'ease-in-out' | |
onTransitionStart : an callback event to invoke at the beginning of the transition, | |
onTransitionComplete : an callback event to invoke at the end of the transition | |
To check if the component is in the middle of a transition, for example to prevent any actions while animation is occuring, | |
add a 'ref' the PageTransition and check for this.refs.myRef.isTransitioning() | |
*/ | |
function makeid() | |
{ | |
var text = "page-transition-styles-"; | |
for( var i=0; i < 20; i++ ) | |
text += Math.floor(Math.random() * 10).toString(); | |
return text; | |
} | |
function addClass(el,className){ | |
if (el.classList) | |
el.classList.add(className); | |
else | |
el.className += ' ' + className; | |
} | |
function removeClass(el,className){ | |
if (el.classList) | |
el.classList.remove(className); | |
else | |
el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); | |
} | |
module.exports = React.createClass({ | |
propTypes : { | |
goToIndex : React.PropTypes.number.isRequired, | |
duration : React.PropTypes.number.isRequired, | |
easing : React.PropTypes.string, | |
onTransitionStart : React.PropTypes.func, | |
onTransitionComplete : React.PropTypes.func | |
}, | |
getInitialState : function(){ | |
return { | |
currentIndex : this.props.goToIndex, | |
isTransitioning : false | |
}; | |
}, | |
getDefaultProps : function() { | |
return { | |
easing : 'ease-in-out' | |
} | |
}, | |
getBaseListStyle : function(style){ | |
return _.merge({ | |
position:'absolute', | |
top : 0, | |
height: '100%', | |
width : '100%', | |
display: 'block', | |
},style || {}); | |
}, | |
getDirection : function() { | |
return this.state.currentIndex < this.props.goToIndex ? "left" : "right" | |
}, | |
isTransition : function() { | |
return this.state.currentIndex !== this.props.goToIndex; | |
}, | |
createStyles : function(){ | |
// create unique ID for this component | |
if(!this.styleElementId){ | |
this.styleElementId = makeid(); | |
} | |
// find or create style element | |
var style = global.document.getElementById(this.styleElementId); | |
if (!style) { | |
style = document.createElement('style'); | |
style.id = this.styleElementId; | |
style.type = 'text/css'; | |
document.getElementsByTagName('head')[0].appendChild(style); | |
} | |
// generate dynamic styles | |
var duration = (this.props.duration / 1000).toString(); | |
style.innerHTML = [ | |
'.__pts-slide-right { transform : translate(100%,0); transition : ' + duration + 's ' + this.props.easing + '; }', | |
'.__pts-slide-left { transform : translate(-100%,0); transition : ' + duration + 's ' + this.props.easing + '; }', | |
].join('\n'); | |
}, | |
componentDidUpdate: function() | |
{ | |
var self = this; | |
this.createStyles(); | |
if(this.isTransition()) { | |
self.state.isTransitioning = true; | |
if(typeof self.props.onTransitionStart === 'function') | |
{ | |
self.props.onTransitionStart(); | |
} | |
// without this timeout, the animation doesn't occur, the DOM doesn't have time to react | |
setTimeout(function(){ | |
var direction = self.getDirection(); | |
var className = "__pts-slide-" + direction; | |
var toElement = ReactDOM.findDOMNode(self.refs.toElement); | |
var fromElement = ReactDOM.findDOMNode(self.refs.fromElement); | |
addClass(toElement,className); | |
addClass(fromElement,className); | |
// this timeout eliminates the old, now invisible, element | |
setTimeout(function(){ | |
removeClass(toElement,className); | |
removeClass(fromElement,className); | |
self.setState({ | |
currentIndex : self.props.goToIndex | |
},function(){ | |
self.state.isTransitioning = false; | |
if(typeof self.props.onTransitionComplete === 'function') | |
{ | |
self.props.onTransitionComplete(); | |
} | |
}); | |
},self.props.duration); | |
},0); | |
} | |
}, | |
isTransitioning : function(){ | |
return this.state.isTransitioning; | |
}, | |
render : function(){ | |
var toElement; | |
var fromElement = this.props.children[this.state.currentIndex]; | |
var containerStyle = { | |
height : "100%", | |
width : "100%", | |
position : 'relative', | |
listStyle : 'none', | |
padding : 0, | |
margin : 0, | |
overflow : 'hidden' | |
}; | |
var toStyle = { | |
left : this.getDirection() === 'left' ? '100%' : '-100%' | |
}; | |
// going into this block means we are prepping a transition | |
if(this.isTransition()) { | |
toElement = <li ref='toElement' style={this.getBaseListStyle(toStyle)}>{this.props.children[this.props.goToIndex]}</li>; | |
} | |
return ( | |
<ul style={containerStyle}> | |
<li ref='fromElement' style={this.getBaseListStyle()}>{fromElement}</li> | |
{toElement} | |
</ul> | |
); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment