Closed ChrisSki closed 10 years ago
Hi @ChrisSki! Here are a couple of thoughts about your question:
render()
function for updating the DOM over time (see example below).componentWillUnmount()
hook. The easiest way to do it is probably to use the TweenLite.killTweensOf()
function to remove all tweening activity for your target.Here's an example (extrapolating a roughly complete component from your snippet):
React.createClass({
getInitialState: function() {
// The gasp-react-plugin allows the tweening of a component's state, which
// eliminates the need to circumvent the virtual DOM and tween actual DOM
// elements directly. Since we are tweening state, we need starting values
// for the tween to interpolate from.
return {
buttonOpacity: 0,
textOpacity: 0,
logoOpacity: 0,
logoY: 0
}
},
componentDidMount: function () {
var tl = new TimelineLite();
// Here, we create our timeline. Notice that we're tweening properties of
// `state` on `this` (the component), and not mutating DOM elements
// directly.
tl.to(this, 0.25, {state: {textOpacity: 1, delay: 0.25}});
tl.to(this, 0.5, {state: {logoOpacity: 1, logoY: 22}});
tl.to(this, 0.5, {state: {buttonOpacity: 1, delay: 1.25}});
},
componentWillUnmount: function () {
// Since this hook will be called right before React removes our component
// from the DOM, this is a good place to kill our tweens.
TweenLite.killTweensOf(this);
},
render: function () {
// Since we're tweening the component state, we will be re-rendering the
// component on each tween update. Here, we calculate the styles for the
// component elements based on the current component state, and then render
// them with those styles.
var buttonStyle = {opacity: this.state.buttonOpacity};
var textStyle = {opacity: this.state.textOpacity};
var logoStyle = {opacity: this.state.logoOpacity, left: this.state.logoY + 'px'};
return (
<div class="button" style={buttonStyle}>
<div class="welcome-text" style={textStyle}>Welcome!</div>
<div class="logo" style={logoStyle}></div>
</div>
);
}
});
Note: if you don't want to tween the component state, but actually tween the DOM nodes directly, then you don't really need this plugin, but you should access the DOM nodes using refs instead of the JS selector functions. You would still kill the tweens in componentWillUnount
using killTweensOf
, but
targeting your refs
:
React.createClass({
componentDidMount: function () {
var button = this.refs.started.getDOMNode();
var text = this.refs.welcomeText.getDOMNode();
var logo = this.refs.logo.getDOMNode();
var tl = new TimelineLite();
tl.to(text,.25, {autoAlpha: 1, delay: .25});
tl.to(logo,.50, {y: 22, autoAlpha: 1});
tl.to(button,.5, {autoAlpha: 1, delay: 1.25});
},
componentWillUnmount: function () {
TweenLite.killTweensOf(this.refs.started.getDOMNode());
TweenLite.killTweensOf(this.refs.welcomeText.getDOMNode());
TweenLite.killTweensOf(this.refs.logo.getDOMNode());
},
render: function() {
<div class="button" ref="started">
<div class="welcome-text" ref="welcomeText">Welcome!</div>
<div class="logo" ref="logo"></div>
</div>
}
});
Hope that helps! I'm going to close the issue, but feel free to ping/respond if you have further questions.
@lettertwo thank you for taking the time to explain that thoroughly. Your explanations make perfect sense and definitely clarify things!
@lettertwo This actually leads me to another interesting issue. How can we tween a component that is being unmounted. For instance, when that component is about to unmount, how can I tween the opacity to 0 before it completely unmounts?
@ChrisSki good question. The short answer is: you can't do that (at least, not from within the component itself).
The long answer is that that you have to make a parent component that can control when the component starts playing its animation and when it gets unmounted. Because componentWillUnmount
is called immediately before the component is unmounted, any changes you make there won't show up in the DOM. So, a parent component must be tasked with knowing that it is going to remove the child component, and then making sure that the child performs its 'outro' behaviors before actually removing it.
React provides one solution as an addon: the ReactTransitionGroup. The basic idea is that you have a parent component that is a <ReactTransitionGroup>
, and your child components implement transition-specific lifecycle hooks to allow the parent to know when it is ok to mount and unmount children.
Basically, you would do your 'outro' animation in the componentWillLeave(callback)
hook, making sure to only call the provided callback
after your animation is complete. (you can also do the same for your 'intro' by putting it in the componentWillEnter(callback)
hook).
Unfortunately, a complete solution is a bit more involved than is reasonable to describe in a github issue thread, but I hope that link provides enough overview of what a general React-based approach would be.
@lettertwo I've used ReactTransitionGroup in the past have understand a bit about the lifecycle. Looks like it's time to roll up the sleeves and dig in deeper. Again, thanks for giving a detailed explanation. If you find a way to do this in the meantime, please check in. And I'll do the same!
@lettertwo I'm noticing that the delay doesn't seem to take effect. are we not calling it properly in the example above?
This may not be an issue, but more of a questions about the best way to handle TimelineLite with components that are about to be unmounted. Currently, I am using the following:
I am not sure how to implement this sort of functionality with this plugin. Is it possible?
And I think the bigger question here, is what happens when I unmount this component? How do we kill these Tweens and avoid any memory issues?