Hi. I've been trying to figure out a simple case:
How to make an element "jump" (scale from 1.0 to 1.1 and back to 1.0), every time it re-renders (initial render, and every time a certain prop changes)? Let's say, a "jumping counter".
Is there any simple case like this? Couldn't figure out how to map Transitions, Springs and Keyframes to this.
This is the furthest I can make it: https://codesandbox.io/s/v8zxlw0q47
Problems that I can't figure out:
Keyframes the right thing to use?config or with a specific delay?async + call or just an array?reset property of the Spring somehow?I have been pondering upon this as well. Not really any examples of passive continuous running pulse animations. Except for https://codesandbox.io/embed/q9lozyymr9. Which does not really respond to state updates as @Kadrian describes.
@Kadrian I can do a working example with the imperiative api.. It seem like cheating though.
@lasseborly Show me! That would be amazing. Maybe we can figure it out.
Have to say it's weird. I like all of the concepts in react-spring, using it feels very powerful. But if a simple case like this feels like cheating, there's room for improvement :-). If not in react-spring itself, then at least in the Readme.
Yeah. Agree. But I'm pretty sure we are just missing the point. 馃槄 Let me get home and I'll share my sandbox.
@Kadrian this is my hack.. https://codesandbox.io/s/qkzx3n7y9q 馃槄
Wow nice! How did you even know that there's an export called controller and AnimatedValue? That's not documented in any of the API refs, right? Now I see what you mean with "hack".
Would really like someone who knows how to use the library to tell us how to do this properly.
@Kadrian it's briefly shown here.
But from then on it was a combination of looking through most usage of react-spring on github and codesandbox to see if anyone else had tried some of the same stuff.
Did not exactly turn out to be that fruitful of a search. So delved into the source. :blush:
Yeah.. Would be lovely :heart:
Ok so for proper visibility:
We still need help here
Here is my attempt: https://codesandbox.io/s/xpqq0ll5nw - I'd be interested to know if this is less or more of a hack.
I created a Keyframes container with one animation that scales to 1.2 and then back to 1, and then used that as the Counter container.
const Counter = Keyframes.Spring({
bounce: [{ to: { scale: 1.2 } }, { to: { scale: 1 } }]
});
// later, in the render function:
<Counter key={this.state.count % 2} state="bounce">{ style => (
<div style={{ transform: `scale(${style.scale})` }}>{this.state.count}</div>
) }</Counter>
The key (ha) was to supply a key prop to the container component in addition to the state prop documented here, so that it treats the animation as a new one every time the count is updated. In a different issue, this behaviour was a problem (https://github.com/drcmda/react-spring/issues/118), but for this case it is the desired behaviour.
Initially I used the count as the key, because that's the value I wanted the Animation to watch. But the modulus of the count is enough to re-render every time the counter changes, and that way we are only dealing with 2 different keyed animations (0 and 1).
Keen to know if there are issues with this approach, or if there is a better way to do this.
That looks a little more like it should! :-) Thanks a lot! I'm wondering: Why isn't it necessary now to use an animated.div?
As far as I can understand, animated.div is called for when using the native flag (docs).
Since that is the recommended approach, I have updated the codepen to use the native flag on the counter. Note that this has required me to import interpolate in addition to animated from the library, since the style must be interpolated manually as far as I can make out.
Also, if your animation only had one step (just scaling from 1 to 1.2 every time, and not needing to go back to 1 right away), you could use the Spring component to render it as usual, with the same key prop that would ensure it re-runs every time. Here's a codepen using a Spring animation with the native flag on. (Note that I didn't import interpolate in this case, because the scale property in this case has an interpolate method, so I could just call scale.interpolate.
I'm very new to this lib too, so hopefully @drcmda can weigh in on all this at some point. I tried to see if the reset prop on Spring was the right one to use in this case (I see that you also asked that in your original question), but the app kept crashing with a 'too many updates' type message. @drcmda if you see this, I would love an example showing the reset prop in action.
I was on a small trip, just getting on top of things. I'd probably do it either with a self-made primitive or keyframes as well. If any of you have suggestions on how to cut down on the boilerplate a little or maybe you have something else in mind, im all ears.
For instance, how about allowing arrays as first class element, just like functions are first class (Keyframes.Spring(next => next({ to: { ... }})):
Keyframes.Spring([{ to: { scale: 1.2 } }, { to: { scale: 1 } }])
I wish i could remove the need for to as well, but it would be too implicit since Spring also has a from as well as all the other props you could use (native, onRest, delay, etc). Maybe "looking" for the presence of all default props and dumping the rest into "to" would be possible ... but such things often turn out messy and hard to type/document.
@Kadrian @cosmophile Correct, the native flag uses a wrapped element (div, span, etc) that interprets the style property (and attributes) differently. Normally react can also accept plain values. But animated.div can take animated-values, they're classes that inform the element of changes so it can skip reacts render passes. For that reason you couldn't do transform: scale(${x}) because x is not a number.
Made some changes to introduce more shortcuts, here's @cosmophile 's example shortened up: https://codesandbox.io/embed/pxqo7113x
Basically now it's just:
const Counter = Keyframes.Spring.to([{ immediate: true, scale: 1.5 }, { scale: 1 }])
<Counter reset native>
So, no need to re-key (which means it should also be faster), Spring.to interpolates props that can be animated into to, while still allowing reserved props (like immediate in this case). And reset actually does something, i didn't do anything before for some odd reason.
This would also work:
const Counter = Keyframes.Spring([
{ immediate: true, to: { scale: 1.5 } },
{ to: { scale: 1 } }
])
The api is documented here: https://github.com/drcmda/react-spring/blob/master/API-OVERVIEW.md#keyframes
@drcmda Hey, thanks a lot for taking the time to solve it. Really cool to see this in action now.
Three questions:
1) Does your example stop working if you're not using the new to-shortcut anymore?
E.g. if you use:
const Container = Keyframes.Spring([
{ to: { immediate: true, scale: 1.5 } },
{ to: { scale: 1 } },
])
2) Why do I need to say scale: 1.5, but then still need to interpolate it "manually"? Can't I just say: to: 1.5 in the Spring then?
3) How could I have arrived there?
In my opinion, I would have had to answer my questions above plus the following ones in the beginning:
reset property or reset the animation with some "state" or some other way?async call or is the array syntax ok?animated.div? Why?In a way, the API is a little too flexible for me, so that I still can't wrap my head around it. Not only when to use what, but also how to use it remains difficult. You introducing another shortcut is not a bad idea, but again adds to the learning curve. I hope that in the future you'll figure out how to make the whole library a little more straightforward to use. Until then, I'll probably go back to plain css animations.
@Kadrian
You only need to interpolate if you use a native spring, the value you receive in the render function is not a number but a class, you can console.log it. You can't drop a class into reacts style-prop. This is exactly what animated.div is there for. When you use native the component will render only once instead of thousands of times. Again, log it and you'll see the difference:
It's easier than you think, all these props you're dealing with are plain Spring props: to, immediate, reset, etc. Keyframes just wraps that spring and orchestrates it. Generally, read the api overview for all the details: https://github.com/drcmda/react-spring/blob/master/API-OVERVIEW.md and for a prop-overview: https://github.com/drcmda/react-spring/blob/master/API.md
Should I use Keyframes?
For this case i think so
Should I use Spring or Transition?
Spring can't run arrays, Transition only tracks lifecycles. You use Keyframes if you need to orchestrate any of these primitives.
How to trigger the animation? (immediate: true? other ways?)
If you use an array or function as the argument to keyframes instead of an object with slots it starts by itself, it doesn't need a "state" prop to trigger. Immediate just means that the animation is immediate, it just jumps to the end state without animation.
Should I use the reset property or reset the animation with some "state" or some other way?
reset means the animation starts from scratch. Without this the animation stays put because nothing changes, you don't supply it with a new state or slot. Using reset instead of re-keying it is faster.
Should I use async call or is the array syntax ok?
Array is shorter
Should I use an animated.div? Why?
Read this: https://github.com/drcmda/react-spring/blob/master/API-OVERVIEW.md#native-rendering-and-interpolation-demo
Most helpful comment
Made some changes to introduce more shortcuts, here's @cosmophile 's example shortened up: https://codesandbox.io/embed/pxqo7113x
Basically now it's just:
So, no need to re-key (which means it should also be faster), Spring.to interpolates props that can be animated into
to, while still allowing reserved props (likeimmediatein this case). Andresetactually does something, i didn't do anything before for some odd reason.This would also work:
The api is documented here: https://github.com/drcmda/react-spring/blob/master/API-OVERVIEW.md#keyframes