I've created a pulsing circle svg element using react-motion. Basically I want a circle to scale larger and fade to nothing, and repeat indefinitely. React-motion spring tends to create the reverse animation as well which I'm trying to eliminate.
I can get it to repeat indefinitely by setting damping = 0 and I don't wan't the reverse animation to happen - that is is if the spring goes from 0 to 1, I don't want it to animate back from 1 to 0. So I've basically made a hack to invert the reverse interpolation so instead of transitioning like 0->1, 1->0, 0->1, 1->0 it does 0->1, 0->1, 0->1, 0->1. It just figures out if the spring is transitioning up or down, and if it's transitioning down it inverts it.
It feels a bit hacky. Is there a better way to approach this? Maybe spring and react-motion isn't the best solution for this type of animation?
import React from 'react';
import {Motion, spring} from 'react-motion';
class Node extends React.Component {
constructor(props) {
super(props);
}
render() {
var springDirectionUp = true; //keep track if spring is going up or down
return (
<g>
<circle cx={this.props.xPos} cy={this.props.yPos} r={this.props.radius} fill={#FFC700} />
<Motion defaultStyle={{s:0}} style={{s: spring(.5, {stiffness: 5, damping: 0})}}>
{({s}) => {
if (s <= 0.01) { // when reaches trough
springDirectionUp = true;
} else if (s >= .99) { // when reaches peak
springDirectionUp = false;
}
let v = springDirectionUp ? s : (1-s); //go from 0->1 twice in a single cycle, ie. replaces the returning 1->0 with 0->1
return (
<circle className="pulse" cx={this.props.xPos} cy={this.props.yPos} r={this.props.radius+this.props.radius*v} fill={#FFC700} style={{opacity: 0.75-v*0.75}}/>
);
}}
</Motion>
</g>
);
}
}
export default Node;
making loop animations is something I've struggled I'd hope to see a better way to do it
you can use onRest to know when the animation is idle, adds a bit of state (demo).
class Node extends React.Component {
constructor(props) {
super(props)
this.state = {
isResting: false,
}
this.handleRest = this.handleRest.bind(this)
}
handleRest() {
this.setState({
isResting: true,
}, () => {
requestAnimationFrame(() => {
this.setState({
isResting: false,
})
})
})
}
render() {
return (
<g>
<circle
cx={this.props.xPos}
cy={this.props.yPos}
r={this.props.radius}
fill={"#FFC700"}
/>
<ReactMotion.Motion
defaultStyle={{ s: 0 }}
style={{
s: this.state.isResting ? 0 : ReactMotion.spring(1, {stiffness: 130, damping: 26})
}}
onRest={this.handleRest}
>
{({ s }) =>
<circle
className="pulse"
cx={this.props.xPos}
cy={this.props.yPos}
r={this.props.radius + this.props.radius * s}
fill={"#FFC700"}
style={{
opacity: 0.75 - s * 0.75,
}}
/>
}
</ReactMotion.Motion>
</g>
);
}
}
ReactDOM.render(
<svg width={400} height={400}>
<Node
xPos={200}
yPos={200}
radius={100}
/>
</svg>,
document.getElementById("App")
)
See this simple Motion wrapper to do Looping and Pulsing animations (check readme/examples)
https://www.npmjs.com/package/react-motion-loop
Most helpful comment
See this simple
Motionwrapper to do Looping and Pulsing animations (check readme/examples)https://www.npmjs.com/package/react-motion-loop