Je voudrais animer entre deux composants où le premier composant disparaît et est supprimé du DOM avant que le composant suivant soit ajouté au DOM et apparaisse. Sinon, le nouveau composant est ajouté au DOM et prend de l'espace avant le l'ancien composant est supprimé. Vous pouvez voir le problème dans ce violon:
http://jsfiddle.net/phepyezx/4
// css snippet
.switch-enter {
opacity: 0.01;
}
.switch-enter.switch-enter-active {
opacity: 1.0;
}
.switch-leave {
opacity: 1.0;
}
.switch-leave.switch-leave-active {
opacity: 0;
}
// React snippet
<ReactCSSTransitionGroup transitionName="switch">
<div key={key} className={className}>{this.text()}</div>
</ReactCSSTransitionGroup>
Une solution inacceptable (pour moi) consiste à masquer le composant d'origine avec css avant de passer au nouveau composant comme on le voit ici:
http://jsfiddle.net/phepyezx/5
// Change to css
.switch-leave {
visibility: hidden;
height: 0px;
width: 0px;
opacity: 1.0;
}
Existe-t-il un moyen de "retarder" la réaction du montage d'un nouveau composant avant de retirer l'original? Je suis ouvert à Velocity ou à une autre bibliothèque pour y parvenir.
Merci
Résolu à l'aide de la méthode de cycle de vie componentWillUnmount()
.
http://jsfiddle.net/phepyezx/9/
Voici le code:
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
const Off = React.createClass({
componentWillUnmount () {
this.props.handleTransitionEnd();
},
render() {
return (
<div className="off button">OFF</div>
)
}
});
const On = React.createClass({
componentWillUnmount () {
this.props.handleTransitionEnd();
},
render() {
return (
<div className="on button">ON</div>
)
}
});
var Switch = React.createClass({
getInitialState: function() {
return {
on: false,
transitionEnd: true
};
},
toggle: function(e) {
this.setState({
on: !this.state.on,
transitionEnd: false
});
},
handleTransitionEnd() {
this.setState({transitionEnd: true});
},
renderOff() {
if (! this.state.on && this.state.transitionEnd) {
return (
<Off key="off" handleTransitionEnd={this.handleTransitionEnd} />
)
}
},
renderOn() {
if (this.state.on && this.state.transitionEnd) {
return (
<On key="on" handleTransitionEnd={this.handleTransitionEnd} />
)
}
},
render: function() {
return (
<div>
<button onClick={this.toggle}>Toggle</button>
<ReactCSSTransitionGroup transitionName="switch">
{this.renderOff()}
{this.renderOn()}
</ReactCSSTransitionGroup>
</div>
);
}
});
React.render(<Switch/>, document.getElementById("switch"));
Et le CSS pertinent:
.switch-enter {
opacity: 0.01;
}
.switch-enter.switch-enter-active {
opacity: 1.0;
transition: opacity 500ms ease-in;
}
.switch-leave {
opacity: 1.0;
}
.switch-leave.switch-leave-active {
opacity: 0;
transition: opacity 500ms ease-out;
}
Vous pouvez obtenir le même résultat efficace avec réponse de Jonny Buchanan qui utilise un positionnement absolu et un délai au lieu de componentWillUnmount()
Une autre solution consiste à faire en sorte que les éléments entrants et sortants occupent le même espace, par exemple en les positionnant tous deux de manière absolue:
<ReactCSSTransitionGroup
className="container"
component="div"
transitionName="switch">
...
.container {
position: relative;
}
.container > div {
position: absolute;
}
http://jsfiddle.net/phepyezx/7/
Vous pouvez utiliser transition-delay
pour attendre que le composant sortant disparaisse avant de faire apparaître le composant entrant, par exemple:
.fade-enter {
opacity: 0.01;
}
.fade-enter.fade-enter-active {
opacity: 1;
transition: opacity 1s;
transition-delay: 1s;
}
.fade-leave {
opacity: 1;
}
.fade-leave.fade-leave-active {
opacity: 0.01;
transition: opacity 1s;
}
Si vous souhaitez retarder le rendu du composant suivant, vous pouvez utiliser quelque chose comme ceci:
import React, { Component } from 'react';
export default class DelayedRender extends Component {
static propTypes = {
delay: React.PropTypes.number.isRequired,
children: React.PropTypes.element,
className: React.PropTypes.string
};
constructor(props) {
super(props);
this.state = {
render: false
};
}
componentDidMount() {
setTimeout(() => {
const delayedClassNames = this.refs.noDelayed.className;
this.setState({
render: true,
classNames: delayedClassNames
});
}, this.props.delay);
}
render() {
const { children, className } = this.props;
return this.state.render ?
<div className={this.state.classNames}>{children}</div> :
<div className={className} ref="noDelayed" ></div>;
}
}
Et dans votre méthode de rendu:
const ROUTE_TRANSITION_TIME = 500;
const views = [];
if (shouldRenderDelayedRoute) {
views.Push(
<DelayedRender delay={ROUTE_TRANSITION_TIME} key="book">
<A ref="book"/>
</DelayedRender>
);
} else {
views.Push(<B key="library"/>);
}
<ReactCSSTransitionGroup
transitionEnterTimeout={ROUTE_TRANSITION_TIME}
transitionLeaveTimeout={ROUTE_TRANSITION_TIME}
transitionName="fade-transition"
>
{views}
</ReactCSSTransitionGroup>