react-spring icon indicating copy to clipboard operation
react-spring copied to clipboard

[bug]: useTrail does not work correctly (spring values are weaker the more trails you have)

Open zroper opened this issue 4 years ago • 7 comments

🤓 Question

What is the proper way to loop a Trail in v9.0? I've had success with the loop prop for Springs but I'm having difficulty using it with a Trail.

const paths = [...]

<Trail
   loop
   items={paths}
   from={{ opacity: 0.2, y: 0, stroke: 'rgba(255,75,75,1)'}}
   to={{ opacity: 1, y: 30, stroke: 'rgba(80,80,80,.35)'}}
>
   {(item, index) => ({ y, opacity, stroke }) => (
      <animated.path
         style={{
            opacity,
            transform: y.interpolate(y => `translateY(${ y}px`),
         }}
         d={item}
         fill="black"
         stroke={stroke}
         stroke-width={3}
      />
   )}
</Trail>

This code runs the full animation once, but on subsequent loops, only the first few elements in the array are animated. I've tried playing around with delay and duration and it seems to improve the animation somewhat but the latter items in the array only partially animate. I also tried initially setting loop to false, then toggling it to true in the onRest function, but this doesn't loop at all.

Here is a link to the sandbox: https://codesandbox.io/s/tender-https-55nmz?file=/src/LoopedTrail.js

zroper avatar Jun 19 '20 16:06 zroper

Seems like a bug that loop={true} doesn't work, but I would need to dig deeper to confirm that.

loop={{ reverse: true }} seems to work as expected, though.

aleclarson avatar Jul 02 '20 13:07 aleclarson

Thanks for looking into this. It does seem like there is weird behavior going on with loop={true}.

zroper avatar Jul 03 '20 03:07 zroper

Not working via hook api either, but seems its only related to usage with state, using to / from works

i.e. this doesn't loop (loding here is from const [loading, setLoading] = useState(true))

 const spinerProps = useSpring({
    rotate: loading ? 360 : 0,
    config: { duration: 1000 },
    loop: true
  });

but this does

const spinerProps = useSpring({
    from: { rotate: 0 },
    to: { rotate: 360 },
    config: { duration: 1000 },
    loop: true
  });

xzilja avatar Apr 12 '21 12:04 xzilja

@IljaDaderko do you have a codesandbox you could share? Preferably using our most up to date version of the library.

joshuaellis avatar Apr 28 '21 17:04 joshuaellis

Could I ask if there's an easy way to stagger a sequence of springs? As far as I can tell, useTrail doesn't fit my use-case, because it seems to decrease the intensity of the springs further down the trail, whereas I wish the springs to act equally, but just with a set time offset between the start of them.

From discord. Possibly helpful

joshuaellis avatar Jun 28 '21 09:06 joshuaellis

It was me that left the above comment. There's a comment earlier up that says

loop={{ reverse: true }} seems to work as expected, though.

but this isn't true. To restate, the intensity of the springs at the end of the trail is reduced.

The code in https://github.com/pmndrs/react-spring/blob/master/packages/core/src/hooks/useTrail.ts has;

if (parent) props.to = parent.springs

And mathematically, I can see why this is happening. As each subsequent strings domain is restricted slightly by it's parent. Say a first spring has gone from 0 to 100, and a second spring is intended to go from 0 to 100 also, but in fact it is more like 0 to 95, because the parent has begun returning towards 0 before the second spring reaches 100. And the value of the 2nd spring is following the parent, rather than the config of the parent with an offset. And multiply the effect for each spring.

I can't see off the top of my head a simple way to make useChain work with loop: { reverse: true } (or {loop: true}, without completely rewriting the implementation.

useTrail is fantastic for the trailing spring effect, but I wonder whether introducing a linear offset config value in combination with useSprings would fit the desired use case of multiple looping springs with an offset better. (I've also tried using duration + progress to simulate an initial delay, but setting progress and looping seems to restrict the domain of the spring values)

midanosi avatar Jun 28 '21 11:06 midanosi

I found a solution to my problem (though I think it's still a little buggy, not sure).
Am sharing here because even though it isn't a solution to this specific useTrail bug, I believe that it's a solution to the same sort of problem.

To get my staggered springs, I set delay: 50 * index in root of config object, then loop: { reverse: true, delay: 0} for loop configuration (which is only referred to at the end of the first animation, which is why the root delay is respected.

midanosi avatar Jun 28 '21 13:06 midanosi