chenglou / react-motion

A spring that solves your animation problems.
MIT License
21.69k stars 1.16k forks source link

provide a easy way to know the percentage of animation? #394

Closed lzl124631x closed 7 years ago

lzl124631x commented 7 years ago

For example, I change a cube's x position using spring, and want the background-color to change proportion to the percentage of animation. Is it possible?

DetweilerRyan commented 7 years ago

It is possible. You can calculate the percent yourself by dividing the interpolated value for x by the destination value of x:


const Cube = (props) => 
  <Motion style={{x: spring(porps.x)}} >
  {
    interpolatingStyle => {
      const percent = interpolatingStyle.x / props.x;
      // return the rendered cube
    }
  }
  </Motion>
lzl124631x commented 7 years ago

I've thought of your solution, but it has several problems

  1. What about the start value of x? I need to store the start value of x. Things get more complicate if the start value of x is a intermediate value of last motion animation (the last motion is interrupted by this current motion)

  2. Your solution gets the x percentage, not the progress (or time) percentage Assuming props.x being 100, start value of x being 0, x being 50, the percent will always be 50%, but the progress percentage might be 10%, 50%, 90%, with different stiffness and damping.

If the wobbling is significant, your x percentage might be 120%, while progress percentage is always in range of [0, 1].


My trick is to set the a motion style percentage: 0 and percentage: spring(1), then I'll get a progress percentage.

But if I chain two animations, I have to use percentage: spring(2) for the second animation... because percentage is already 1 at the end of the first animation

nkbt commented 7 years ago

There is no such thing as "percentage" of spring animation. Every single step depends on the previous one and the target value. Since target value can be changed anytime, animation is potentially indefinite and so you cannot say for sure when it is 50% or 90%.

This is one of essential differences between Spring-based animations and time-based.

I reckon you can approximately calculate overall duration given that target values will never change, just find a formula to use for this (having spring params, start and stop values). This can be done totally outside of ReactMotion component itself

lzl124631x commented 7 years ago

@nkbt Thanks for your advise. Actually I just want to animate color while animating offset, but React-Motion doesn't support it. So I have to use "percentage" to interpolate color. It would be great if React-Motion supports animating color.

nkbt commented 7 years ago

@lzl124631x you can animate arbitrary numbers with react motion, and derive color from that number. Effectively:

 <Motion style={{value: spring(255)}} >
  {({value}) => {
    const color = `rgb(${value}, ${value}, ${value})`;
  }}
</Motion>
tannerlinsley commented 7 years ago

This brings up a very valid opportunity for react motion. Indeed, you can easily spring to and from arbitrary numbers, but technically you wouldn't be able to interpolate anything that is a non number without keeping a detailed history of changes from non integer to non integer, and even then you are still a few complicated steps away from coercing that backing number into an interpolate that supports non numbers.

That's a lot to take in and think about, but for me its becoming a large problem. If we could solve the non number interpolation problem, anything would be possible.

sompylasar commented 7 years ago

@tannerlinsley The "non-numbers" are effectively number-based if they can be interpolated. It's just the question of functional mapping from number to non-number, e.g. this:

const color = `rgb(${value}, ${value}, ${value})`

But the notion of "percentage of animation", i.e. how close are you to the "end moment in time of the animation" (versus to the "target value of the animation"), is not applicable to physics-based animations which Motion provides by default with spring.

tannerlinsley commented 7 years ago

@sompylasar Right. The concept is very straightforward to interpolate anything using an integer, but the architecture and current api for react-motion doesn't make this very easy to do with strings, hexColors, paths, etc.

A good example of this is animating a string. The amount of overhead the user would have to take on to build a linear conversion scale from one string to another is massive. Luckily, d3-interpolate can handle that for you.

If we assume that, then the problem boils down to finding a reliable way to construct the interpolator and percentage on every value change:

const interpolator = d3.interpolate(currentString, newString)
const percentageFromAtoB = ??? // Not currently possible with the given api
const val = interpolator(percentegeFromAtoB)

Even though it's not natural for a physics based animation system to know of the percentage between the current and destination value, it is possible. You need to be able to:

You should check out https://github.com/chenglou/react-motion/issues/153#issuecomment-284824453. It's a (very shrude and quick proof of concept) abstraction that does exactly this. I would love some feedback.

tannerlinsley commented 7 years ago

This block initially gave me the impression that it would be fairly easy to accomplish this, but it makes a very limiting assumption that the values you want to interpolate between are binary and that you must be explicit with both the from and to. Still a great block, but stopped halfway to where I was hoping it would land :)

sompylasar commented 7 years ago

@tannerlinsley Good job there! I meant one cannot get the time-based percentage because the time moment when physical animation ends is not known in advance, without premodeling it. The value-based percentage can definitely be obtained, that's what is in the block you've shown. And it might overshoot in case of a spring (the complex easing curves can, too, so it should be fine).

nkbt commented 7 years ago

Added generic issue to cover this in README https://github.com/chenglou/react-motion/issues/494

If anyone wants to PR - that would be super awesome!