motiondivision / motionone

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

[Feature] Stopping/changing a scroll feature #141

Open bakura10 opened 2 years ago

bakura10 commented 2 years ago

Hi :),

I am trying to use the scroll gesture, but we have an additional constraint where the offset may change depending on the breakpoint:

offset: [`-${SOME DYNAMIC VALUE}px start`, "end start"]

This means that I would either need to be able to:

I checked the doc and the source but it does not seem this is possible. Am I missing something? :)

CAWeissen commented 1 year ago

@bakura10 The way I've dealt with this is to use the web animations API directly to find the associated animations, remove them, and then recreate them.

const resizeObserver = new ResizeObserver(entries => {
  for (const entry of entries) {
    const anims = animatedElement.getAnimations(); // Get all animations on the element
    anims.forEach(anim => anim.cancel()); // Remove previous animations

    // Recreate your animation here
    const newSequence = [[animatedElement, newKeyframes, animationOptions]];
    scroll(timeline(newSequence), scrollOptions);
  }
});

resizeObserver.observe(wrapperElement);
bakura10 commented 1 year ago

Thanks a lot for the trick @CAWeissen .

@mattgperry , may it be possible to abstract this with a simpler "stop" method that can be called on the scroll object?

stepanjakl commented 11 months ago

+1 for a simple way to stop the scroll animation (remove the scroll object/instance)

jankohlbach commented 10 months ago

yeah, would love to see that, need to create a different scroll timeline on mobile and desktop, on currently they are just both there after resize :(

Ambient-Impact commented 7 months ago

Based on my testing and poking through the code, it looks like you can pass a Controls instance to scroll(), which gives you the ability to stop it via Controls.cancel():

/**
 * scroll() options object.
 *
 * @type {Object}
 *
 * @see https://motion.dev/dom/scroll
 */
const scrollOptions = {
  // scroll() options go here.
};

/**
 * Motion Controls object for the current animation.
 *
 * @type {Object}
 *
 * @see https://motion.dev/dom/controls
 */
let animationControls = animate(function(value) {

  // Do stuff with value.

}, {});

// Pause the animation so it doesn't progress except by being scrubbed on
// scroll. This is the same thing that Motion.scroll() does internally
// when it creates an animation if you don't pass it an existing
// animation.
animationControls.pause();

scroll(animationControls, scrollOptions);

Then you can later call animationControls.cancel() and it stops triggering the callback. Internally, it looks like Motion tries to only ever have one scroll listener on an element, such as the document, and automatically removes the listener if all handlers are stopped/cancelled.