Skip to content

Instantly share code, notes, and snippets.

@miguelludert
Created December 24, 2015 18:37
Show Gist options
  • Save miguelludert/ad456cb6a290601989aa to your computer and use it in GitHub Desktop.
Save miguelludert/ad456cb6a290601989aa to your computer and use it in GitHub Desktop.
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