motiondivision / motionone

https://motion.dev
MIT License
2.89k stars 52 forks source link

[Bug] Initial transform values are not used for the initial keyframe #117

Open xaviergonz opened 2 years ago

xaviergonz commented 2 years ago

1. Describe the bug

Given:

Basically it seems the initial transform values are not used as initial keyframes (scale, rotate, x, y, z...) like it does for all other properties.

2. IMPORTANT: Provide a CodeSandbox reproduction of the bug

https://codesandbox.io/s/brave-monad-f9u188?file=/src/App.tsx

3. Steps to reproduce

Steps to reproduce the behavior:

  1. Open the sandbox
  2. Observe the initial style (scale 0.5, opacity 0.5)
  3. Wait for the animation to trigger
  4. You will see the opacity is animated properly, but the scale is not

4. Expected behavior

The element fades in the opacity and animates the scale from 0.5 to 1

6. Browser details

Chrome 103, OSX 12.5

trafnar commented 2 years ago

I'm also having this problem, and noticed it seems to happen with other transform values such as translateX. The library seems to always treat the start value as whatever the default value would be, not the value set in my styles.

trafnar commented 2 years ago

A possible workaround is to animate to your start value with duration 0, then do your animation, something like this:

// use await, and use the .finished property of the returned animation controls on a 0ms animation
await animate(myElement, {scale:0.8}, {duration:0}).finished

// then do the animation to scale:1
animate(myElement, {scale:1}, modalScaleEase)
legowhales commented 2 years ago

Actually the best way would be to use arrays like this:

animate(myElement, {scale: [0.8, 1]})
fairbairn commented 1 year ago

It "should" read from the initial values sitting in the style, and then transform x and y from there; instead, it animates to the first x, y from a 0,0 default location.

Framer-Motion has a nifty option called initial which you can set to false, which internally handles placing the item at that location without animating.

The difference is the visual display - since framer-motion wraps the animated element, it can place the object in the correctly transformed position upon the first render.

Using motion one in React, requires that we have the component mounted first, so we have a ref to pass into the animate() function upon mount. Unfortunately, because of that delay, it's a bit janky, when the goal is to "place" and animate from there.

I'd be curious if there were other options.

--

I will add that by breaking out our component into a React.Component instead of a functional component, in our case, it allowed us to animate to the original location with duration at 0 on mount, and then on the update, we used our normal duration.

There is a delay of course, but it does allow the effect to happen from the intended origin (x,y) location.