• instagram
  • twitter

SpringTree

logo
leaves
leaves
leaves

An awesome animation library unlike any other

Ditch the time based animations, and let physics do the complicated stuff for you.

If you’ve ever written a css animation that’s more complex than fading in- and out the opacity of an element, then you probably know that past this threshold it gets pretty hard to make things look and feel good. Sure, you can spend your time meticulously writing keyframes to make the animation look just right, but this is a lot of effort for often mediocre results.

If you were reeled in by this title and you don’t like react, well I’m sorry - the library I am demoing today is a react-exclusive: React Spring. This library caught my attention when I first came across it, with the name containing the word spring and all, and has been living in the back of my head for a while. Lately I’ve been using it for one of my private projects and it’s been a blast to work with.

React-spring ditches time-based animations (well, not completely, but you most likely won’t need them). Instead, it allows us to work with physics based animations. To be specific: it simulates a spring and takes two input properties: friction and tension.


Our end result will look like this: 

Tree growing.gif

First, let’s prep some things


Now, sadly I didn’t have an SVG of the SpringTree logo laying around so I recreated it using Figma and split it up into three separate components: trunk, branches and leaves. Trunk contains a vector representing the tree trunk, branches is an array of vectors each representing one of the branches along with the side of the tree they’re on, and leaves - well I think you noticed the pattern already. 

Which leaves (haha, get it?) us with the following prepared data:

// Left out the path strings because they're too long
const trunk = <path stroke="#CF9C52" className="origin-bottom" d="..." fill="#CF9C52"/>;

const branches = [
 {side: 'right', path: '...'},
 {side: 'left', path: '...'},
 {side: 'left', path: '...'},
]

const leaves = [
 {x: 315.798, y: 151.273, width: 133.495, height: 128.727, fill: '#18B07B'},
 {x: 175.652, y: 68.3384, width: 59.7879, height: 57.404, fill: '#0F9748'},
 {x: 389.697, y: 96.4445, width: 40.5253, height: 39.3333, fill: '#18B07B'},
 // ...and a bunch more leaves!
]

Applying these dimensions and values to svg elements we get the following result:

image.png


Beautiful, aside from the slightly deformed trunk you can barely tell I copied it using figma. Now, let’s get to animating.

Animating the trunk

First of all, I’d like to animate the growing trunk. Using react-spring this should be easy enough.

 const [trunkLength, setTrunkLength] = useState(0);

 const growAnimation = useSpring({
   from: {
     // Use the popular strokeDashArray / strokeDashOffset
     // combination to animate the stroke being drawn
     strokeDasharray: trunkLength,
     strokeDashoffset: trunkLength,
     fillOpacity: 0,
     strokeWidth: 3,
   },
   to: [
     { strokeDasharray: trunkLength, strokeDashoffset: 0},
     // After drawing the stroke, fill in the shape
     { fillOpacity: 1 },
   ],
 });

 const trunk = <animated.path ref={(ref) => {
   if(ref) {
     setTrunkLength(ref.getTotalLength());
   }
 }} stroke="#CF9C52" style={growAnimation} className="origin-bottom" d="..." fill="#CF9C52"/>;

The useSpring hook is a very flexible tool. For the trunk we’re using it to draw the outer stroke, and fill in the shape. Note that we’re assigning an array to the “to” property - this will cause two animations to be run in sequence.

Note that we’re hooking into the path reference and using it to call get the total length of the path, which represents the length of the outer stroke. 

The properties we’re animating here are strokeDashArray and strokeDashOffset, two properties which when used in tandem can be used to animate the stroke being drawn from start to end.

Below that we’re defining the “trunk” element. Note that we’re not using a regular path here, but we’re instead using the path subcomponent from the “animated” variable (imported from react-spring). Then we can just pass the spring along to the style property of the path and we should have ourselves a nice little animation!

Let’s see what this results in: 

Trunk growing.gif


Nice! But that’s just a tree trunk. We’ve got a few more steps to go through.

Animating the branches

Next, I want to animate the branches sort of “folding” out of the tree. To accomplish this I came up with the following configuration. Now things get a bit more complicated.


 const branches = [
   {side: 'right', path: '...' },
   {side: 'left', path: '...' }
   // more branches
 ]

 const branchTransitions = useTransition(branches, {
   from: {
     opacity: 0,
   },
   enter: {
     opacity: 1,
   },
   config: config.gentle,
 });

 const branchElements = branchTransitions(({opacity}, item) => (
   <animated.path d={item.path} fill="#CF9C52" className={item.side === 'left' ? 'origin-bottom-right' : 'origin-bottom-left'} style={{
     opacity,
     transformBox: 'fill-box',
     transform: opacity.to((o) => {
       const degrees = item.side === 'left' ? -90 : 90;
       const translatePx = item.side === 'left' ? 10 : -10;
       const px = translatePx - (o * translatePx) + 'px';

       return `rotate(${(degrees) - (o * degrees)}deg) translate(${px}, ${px}) scale(${o})`;
     }),
   }}/>
 ));

This time we’re using the useTransition hook from react-spring - which allows us to transition arrays. Note that we’re only specifying a transition for the opacity property here. How is this going to make the branches fold out of the trunk?

React-spring properties are more than just a single animation. They can also be used to interpolate other values. Since the opacity value slowly transitions from 0 to 1, it’s really easy to use it to animate other values as well. 

In the style definition in branchElements we’re using the opacity value to interpolate each branch's transform. The side property of each branch comes in handy now because it allows us to adjust the direction of the transform. Let’s see what this results in:

Branches growing.gif

Cool! If you’re wondering how it looks so springy, it’s because when we defined the transition we specified the config property with a friction and a tension. Now let’s animate the leaves.

Animating the leaves 

Finally, the colorful part. The code for animating the leaves is as follows:

const leaves = [
   {x: 315.798, y: 151.273, width: 133.495, height: 128.727, fill: '#18B07B'},
   // And a bunch more leaves...
 ];

 const leavesTransitions = useTransition(leaves, {
   from: {
     transform: 'scale(0)',
   },
   enter: {
     transform: 'scale(1)',
   },
   config: config.wobbly,
   trail: 100,
 });

 const leavesElements = leavesTransitions((props, {x, y, width, height, fill}) => (
   <animated.rect className='origin-center' x={x} y={y} width={width} height={height} fill={fill} style={{
     transformBox: 'fill-box',
     ...props
   }}></animated.rect>
 ));

If you’re starting to get the hang of it, this should pretty much speak for itself. What is interesting though is the trail property on the transition. This causes a 100 millisecond delay between each animation being fired, which results in this nice pop-in effect:

Leaves growing.gif

And now, let’s chain all of them together using the useChain hook! https://react-spring.io/hooks/use-chain

Tree growing.gif

I’ll have to agree with my colleague Willem in that Svelte is probably a nicer framework to work with than React, but come on, even he has to admit that the react-spring library is pretty damn cool.

Source code: https://github.com/joeryoost/spring-animation-demo