framer / motion

Open source, production-ready animation and gesture library for React
https://framer.com/motion
MIT License
23.84k stars 810 forks source link

[BUG] Drag release triggers all animating variants #1831

Open Russell-Newton opened 1 year ago

Russell-Newton commented 1 year ago

Hey there, I'm pretty new to React, particularly framer-motion, so it's very likely this came up due to bad design patterns.

Describe the bug

I wanted to create a sort of slideshow but with divs instead of images. Specifically, I wanted to be able to swipe to change Chakra UI Tabs. I based it off of the slideshow example: https://codesandbox.io/s/framer-motion-image-gallery-pqvx3. I didn't do the same strategy of a single element with index-based key and src (children in the case of a div) because I didn't want the div contents to be removed from the React tree, so I instead created some flags to indicate which div should be centered and which div should be exiting.

To account for all of this, I saw on the multiple variants documentation (https://www.framer.com/docs/animation/##multiple-variants) that the later variants take precedence, so I created 3 variants:

  1. One that applies base values for any divs not being animated, exiting, or centered
  2. A second that overwrites all the base values with ones for the div currently exiting view
  3. A third that keeps the currently visible div in the center of the screen

I noticed that when releasing the drag on the div centered in the screen, all of the variants complete one after the other very shortly before doing anything with the correct one.

In the CSB, I logged which variant completes with the onAnimationComplete handler. If you drag the centered box around and let go, you'll see in the console the color of the box and the variant that completed. All 3 variants are logged. If you swipe the box to the side to switch the focused box, the "base" variant won't complete.

Other weird things:

Even if there is only a single variant that takes the place of all 3:

Provide a CodeSandbox reproduction of the bug

https://codesandbox.io/s/musing-faraday-e6q75o?file=/src/App.tsx

4. Steps to reproduce

  1. Load the CSB and clear the console.
  2. Hold down on the red box and try to drag it around. It may not actually move.
  3. Release the red box, and see that all 3 variants triggered the onAnimationComplete handler for the red box.
  4. Swipe the red box left, and you can try with the other boxes.
  5. To see the red box not animate properly after the first drag that doesn't move the box, refresh the page. Flick the red box to the right. It won't re-center like it should. If the drag velocity at the end is 0, it will re-center properly.

5. Expected behavior

Releasing the in-view box without a swipe should not result in any of the variants triggering the complete handler immediately. A variant should only trigger the complete handler after the values to animate have completely been set.

The first drag on a box should move the box.

The first drag on the leftmost box should not stop the re-centering animation if it is a right swipe. Likewise, the first drag on the rightmost box should not stop the re-centering animation if it is a left swipe.

6. Video or screenshots

https://youtu.be/wEWZz3o0Tj0

7. Environment details

OS Name: Microsoft Windows 10 Home Version: 10.0.19045 Build 19045 Browser Name: Opera GX Browser Version: LVL4 (core: 93.0.4585.52) Chromium Version: 107.0.5304.110

Also occurs on: Google Chrome (108.0.5359.125) Microsoft Edge (108.0.1462.54)

AshConnolly commented 1 year ago

Having the same issues. Thanks for raising the bug.

For me I also tried dynamically setting drag props, based on an isChangingSlide prop like -

dragSnapToOrigin={isChangingSlide ? false : true}
dragElastic={isChangingSlide ? 0 : 0.5}
dragTransition={{
  bounceStiffness: isChangingSlide ? 0 : 400,
  bounceDamping: isChangingSlide ? 0 : 10,
}}

So that if a slide is changing, it doesn't snap back to the middle as it changes slide. It simple fades out in place. This however didn't work.

I'm aware if you want to pass state to animations you need to use variants and a custom prop like:

variants={slideVariants}
custom={isChangingSlide}

I've used the approach elsewhere successfully. However, the docs are quite vague and don't explain the API of available options when using variants. So it's not clear if the drag properties can be used in variants, and I've not seen an example of it anywhere online.