Animating particles using React Motion

Written by saada | Published 2017/01/01
Tech Story Tags: javascript | animation | react | motion | chrome-dev-tools

TLDRvia the TL;DR App

While working on a personal open-source project Container Hive, I faced quite a few challenges to get particles animating correctly between each Docker container. Container Hive tries to help you visualize how everything fits together between your services. Here’s what that currently looks like…

Each particle is an SVG circle that looks like…

<svg width='8' height='8'><circle cx='4' cy='4' r='4' fill='darkcyan' /></svg>

Since I was using React, I naturally leaned towards trying out React Motion.

Challenge I — Debugging Leaks while DevTools Crashes

That moment when you refresh the page and then after some time passes, you see it…

This was really frustrating because, not only did the tab crash, but also the DevTools window itself. I tried many approaches to profile my scripts and ended up coming up with an interesting hack. I open the console, and when the time was right, I typed debugger and BOOM! I paused everything. All the animations stopped, the React lifecycle, React Motion, everything! I also made sure I had the Timeline tab open in the DevTools to start recording performance on page refresh. I was able to look at all the metrics and debugged performance of my scripts as well as any cpu/memory leaks.

Challenge II — TransitionMotion’s defaultStyles don’t work (bug)

Using TransitionMotion is great for handling multiple elements animating together and having control over mounting and unmounting of each component. But I wasn’t able to get it working using react-motion@0.4.7. The way I approached it initially was by making use of defaultStyles as the initial positions of each particle and then set styles as the final transition styles after initially starting from the defaultStyles.

<TransitionMotiondefaultStyles={particles.map(particle => ({key: particle.key,style: {translateX: particle.x1,translateY: particle.y1,opacity: 0}}))}styles={particles.map(particle => ({key: particle.key,style: {translateX: spring(particle.x2, particleSpring),translateY: spring(particle.y2, particleSpring),opacity: spring(1, particleSpring)}}))}>{interpolatedStyles =><div>{interpolatedStyles.map(({key, style: {opacity, translateX, translateY}}) => {return <svg key={key} width='8' height='8' style={{transform: `translate3d(${translateX}px, ${translateY}px, 0) scale(1)`,marginTop: `${(50 / 2) - borderWidth / 2}px`,marginLeft: `${(50 / 2) - borderWidth / 2}px`,opacity: opacity,position: 'absolute',zIndex: 1000}}><circle cx='4' cy='4' r='4' fill='yellow' /></svg>})}</div>}</TransitionMotion>

What happened was that the particles would just stay at the final position and never go through the animation phase at all. Almost like the defaultStyles didn’t really do anything.

Solution: After three days of trying various solutions, the one that worked was to use the willLeave prop and performed all the animations by returning the final state in its callback. Effectively, ignoring the defaultStyles property. Here’s the working snippet.

<TransitionMotionwillLeave={particle => {return {translateX: spring(particle.data.x2, particleSpring),translateY: spring(particle.data.y2, particleSpring),opacity: spring(1, particleSpring)}}}

styles={particles.map(particle => ({key: particle.key,data: particle,style: {translateX: particle.x1,translateY: particle.y1,opacity: 0.5}}))}>{interpolatedStyles =><div>{interpolatedStyles.map(({key, style: {translateX, translateY, opacity}}, index) => {return <svg key={key} width='8' height='8' style={{transform: `translate3d(${translateX}px, ${translateY}px, 0) scale(1)`,marginTop: `${(50 / 2) - borderWidth / 2}px`,marginLeft: `${(50 / 2) - borderWidth / 2}px`,opacity: opacity,position: 'absolute',zIndex: 1000}}><circle cx='4' cy='4' r='4' fill={allColors[index % allColors.length]} /></svg>})}</div>}</TransitionMotion>

By getting rid of defaultStyles altogether and treating styles as the initial state of the animation, everything worked perfectly. I filed the issue on Github below.

TransitionMotion defaultStyles don't reflect initial state of animation · Issue #416 · chenglou…_react-motion - A spring that solves your animation problems._github.com

Conclusion

React Motion is awesome and provides really powerful tools for animation, but it still has a few gotchas and documentation to iron out. If you use Docker in your stack, give Container Hive a try. It’s a fun and interesting sight that gives you intuition on microservice networks.


Published by HackerNoon on 2017/01/01