React Spring - useTrail delay issues

1.4k views Asked by At

Currently been using react-spring to do our animations in our app; unfortunately, animation is not something I excel in and the design our designer gave us for our new logo is leaving me stumped on implementation. It is pretty easy with plain old JS but implementing it in react-spring has proved a challenge that I can not get past.

The end goal for the animation is to look like this:

https://codepen.io/darylginn/pen/GRqZxBZ

Currently, I am up to this stage:

import React from "react";
import { useTrail, animated } from "react-spring";

const Loader: React.FC = () => {
    // Animations
    const paths = [
        {
            id: 1,
            color: "#466FB5",
            d: "M90.6672 33H16L53.3336 96.4409L90.6672 33Z",
        },
        {
            id: 2,
            color: "#0093D3",
            d: "M53.3347 96.4443H128.002L90.6683 33.0034L53.3347 96.4443Z",
        },
        {
            id: 3,
            color: "#53BFA2",
            d: "M128.001 96.3701H53.3336L90.6672 159.811L128.001 96.3701Z",
        },
        {
            id: 4,
            color: "#93C83F",
            d: "M90.6675 159.892H165.335L128.001 96.4417L90.6675 159.892Z",
        },
        {
            id: 5,
            color: "#58B647",
            d: "M165.334 159.892H90.6664L128 223.333L165.334 159.892Z",
        },
        {
            id: 6,
            color: "#F2E90B",
            d: "M202.667 96.4436H128L165.334 159.894L202.667 96.4436Z",
        },
        {
            id: 7,
            color: "#FBB12C",
            d: "M128.001 96.4443H202.668L165.335 33.0034L128.001 96.4443Z",
        },
        {
            id: 8,
            color: "#FF5E8D",
            d: "M240 33H165.333L202.666 96.4409L240 33Z",
        },
    ];

    const trail = useTrail(paths.length, {
        from: {
            scale: 1,
        },
        to: async (next: any) => {
            while (1) {
                await next({ scale: 1.5 });
                await next({ scale: 1 });
            }
        },
    });

    return (
        <div style={{ width: "200px", height: "200px" }}>
            <svg
                width="72"
                height="72"
                viewBox="0 0 256 256"
                fill="none"
                xmlns="http://www.w3.org/2000/svg"
                style={{ overflow: "visible" }}>
                {trail.map(({ scale }, index) => {
                    const path = paths[index];
                    return (
                        <animated.path
                            key={path.id}
                            fill={path.color}
                            d={path.d}
                            style={{
                                transformOrigin: "center",
                                transform: scale.interpolate((s: any) => `scale(${s})`),
                            }}
                        />
                    );
                })}
            </svg>
        </div>
    );
};

The main issue I am at now is the scale of each triangle in the SVG needs to happen one after another, but nothing I do with the useTrail make this happen.

I did try adding a delay like this to the useTrail

delay: (key: any) => key * 200

But the delay doesn't even seem to make a difference. If someone could help make sure each triangle finish it sequences before the next one starts that would be great.

Bonus if you can also help me add the colour change as seen in the original design (see link).

I have tried posting in the react-spring community but got no replies

1

There are 1 answers

0
Peter Ambruzs On

I would change the useTrail to one useSpring for all the triangles. If you change the x value from 0 to 8, then you can use interpolate and a range for each triangle to change. For example for the second triangle you can use range:[0,1,1.5,2,8],output:[1,1,1.5,1,1]. It means that when x is between 1 and 2 it will change the scale from 1 to 1.5 to 1 and all other places it will remain 1.

  const props = useSpring({
    from: {
      x: 0
    },
    config: {
      duration: 4000
    },
    to: async (next: any) => {
      while (1) {
        await next({ x: 8 });
        await next({ reset: true });
      }
    }
  });

I also added the color interpolation.

    {paths.map((path, index) => {
      const colors = [];
      for (let i = 0; i < 8; i++) {
        colors.push(paths[(i + index) % 8].color);
      }

      return (
        <animated.path
          key={path.id}
          fill={path.color}
          d={path.d}
          style={{
            transformOrigin: "center",
            transform: props.x
              .interpolate({
                range: [0, index, index + 0.5, index + 1, 8],
                output: [1, 1, 1.5, 1, 1]
              })
              .interpolate((x) => `scale(${x})`),
            fill: props.x.interpolate({
              range: [0, 1, 2, 3, 4, 5, 6, 7, 8],
              output: colors
            })
          }}
        />
      );
    })}

I have an example. There is some glitch with the center triangle. But I think you get the idea.

Example: https://codesandbox.io/s/animate-triangles-sequentially-with-interpolating-and-range-oer84