Animated React Metaballs
source link: https://www.tuicool.com/articles/hit/juIJfev
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Animated React + d3 Metaballs
Some time ago I wrote about Advanced Metaballs , describing their utility and outlining an algorithm to compute an SVG path given a set of circles. However, I did not include sample code or information on animation.
That’s where React Metaballs comes in. It’s a simple component built using React and d3. Below is an overview of the project, how to use it, and a bit on my attempts at animation.
react-metaballs
You can find a demo of react-metaballs , as well as the package on npm , and source code available on GitHub .
Installation
npm install react-metaballs
Usage
Once installed, the Metaballs
component can be imported as follows
import ReactMetaballs from 'react-metaballs'
Data is formatted as an array of objects containing three values:
const circles = [ { cx: 200, cy: 100, r: 64 }, // ... ];
cx
— center on the x-axis
cy
— center on the y-axis
r
— radius
The coordinates are relative to the whole, meaning a rectangle is constructed around all circles creating the SVG viewBox
. It then constructs a single path
element around all circles using bezier curves .
class App extends Component { render () { return ( <Metaballs easement={d3.easeBackOut} circles={circles} /> ) } }
The final output renders an svg
element that scales with it’s contain as defined by the preserveAspectRatio
attribute. That’s the scalable in SVG, we’ve defined a vector that the browser knows how to scale up or down.
Animation
If you’ve held a reference to the Metaballs
element, there’s an updateCircles
method that takes care of transitioning between two sets of data.
For example, to transition between two states:
this.metaballRef.current.updateCircles(newCircles)
The original transitions were written with one line in CSS, but this approach was abandoned because of compatibility issues.
path { transition: d 300ms ease-in-out; }
If only animation were this simple. This actually works in Google Chrome, but only Google Chrome :( For other, modern browsers there is the SVG animate
element, but this too has compatibility concerns . Other guides on React SVG Animations suggest requestAnimationFrame
, but this seemed to likely to cause issues and too difficult to manipulate animation properties like easing.
Enter d3
(pun intended)! d3
comes with tons of options, and has supports animating the d
property easily . By creating a component that encapsulates a path
element, animating the d
property was easy:
componentWillReceiveProps(nextProps) { if (this.props.path !== nextProps.path) { let { duration, easement = d3.easeLinear } = this.props; this.path.transition() .ease(easement) .attr('d', nextProps.path) .on('end', () => this.setState({ path: nextProps.path })) .duration(duration); } }
For the purists, this violates all the rules . From a d3 perspective, we’re not really binding to data just using d3 as a tweening library. From a React perspective, we’re manipulating the DOM directly and using the soon-to-be-deprecated componentWillReceiveProps
method. But there certainly are others using React + d3 in this way, and with great results.
That’s it for now. I hope to explore react-move
in the future, which promises to wed React + d3 in a manner consistent with both frameworks. Credit to Varun Vachhar for his code and article on the mathematics of Metaballs .
You can find source code on GitHub . For ideas on styling and use cases, see my previous article on Pretty Metaballs .
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK