Skip to content

Instantly share code, notes, and snippets.

@jonkwheeler
Created June 26, 2018 14:31
Show Gist options
  • Save jonkwheeler/cdd462e0343beec85a3b5c3a4dda0413 to your computer and use it in GitHub Desktop.
Save jonkwheeler/cdd462e0343beec85a3b5c3a4dda0413 to your computer and use it in GitHub Desktop.
React: Animation component using GSAP to interval count up
// @flow
import React, { PureComponent } from 'react';
import { TweenMax, Circ } from 'gsap';
import Waypoint from 'react-waypoint';
type CounterProps = {
min: number,
max: number,
duration: number,
afterText: string,
beforeText: string,
waypointBottomOffset: string
};
type CounterState = {
count: number
};
class Counter extends PureComponent<CounterProps, CounterState> {
static defaultProps = {
min: 0,
max: 500,
duration: 1.5
};
constructor(props) {
super(props);
this.animateCounter = this.animateCounter.bind(this);
this.updateCounter = this.updateCounter.bind(this);
this.updateCounterTween = null;
}
state = {
count: this.props.min
};
componentWillUnmount() {
// Check if the TweenMax exists yet, if so, kill the tween on unmount
if (this.updateCounterTween !== null) this.updateCounterTween.kill();
}
updateCounter(self: any, prop: number) {
this.setState({ count: Math.ceil(self.target[prop]) });
}
animateCounter(direction: string, duration: number) {
/**
* Animate a number up or down in value
*
* @param direction {string} up|down *Optional
* @param duration {number} *Optional
*/
let end,
start;
if (direction === 'up') {
start = parseInt(this.props.min, 10);
end = parseInt(this.props.max, 10);
} else {
start = parseInt(this.props.max, 10);
end = parseInt(this.props.min, 10);
}
const counter = {
val: start
};
this.updateCounterTween = TweenMax.to(counter, duration, {
val: end,
onUpdate: this.updateCounter,
onUpdateParams: ['{self}', 'val'],
ease: Circ.easeOut
});
}
render() {
return (
<span className="counter">
<Waypoint
onEnter={this.animateCounter.bind(this, 'up', this.props.duration)}
onLeave={this.animateCounter.bind(this, 'down', this.props.duration)}
bottomOffset={this.props.waypointBottomOffset}
scrollableAncestor={window}
/>
{this.props.beforeText && <span className="before-text">{this.props.beforeText}</span>}
<span className="count-this">{this.state.count}</span>
{this.props.afterText && <span className="after-text">{this.props.afterText}</span>}
</span>
);
}
}
export default Counter;
@jonkwheeler
Copy link
Author

2018-06-26 15_30_19

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment