davidjerleke / embla-carousel

A lightweight carousel library with fluid motion and great swipe precision.
https://www.embla-carousel.com
MIT License
5.91k stars 177 forks source link

[Feat]: Continuously running carousel (ticker, marquee, ..) #114

Closed openscript closed 7 months ago

openscript commented 3 years ago

Hello everybody

I'm using Embla already in a project and it's great. Thank you for the amazing work!

Now I have the use case to build a carousel which runs continuously like a "stock market" ticker. So no speed up and slow down. Do you think it's easy to build something like that with Embla? Where would be a good starting point?

davidjerleke commented 3 years ago

Hello Robin (@openscript),

Thank you for your question. Is the CodeSandbox in this conversation what you’re looking for?

Let me know if I understod you correctly and it did help.

Best, David

openscript commented 3 years ago

Hello David (@davidjerleke)

Thank you for the very quick response.

The CodeSandbox shows exactly what I want, except that it doesn't start running after it has been dragged, but I think it's not to hard to implement this for the settle hook? :)

davidjerleke commented 3 years ago

Hi again Robin (@openscript),

Yes, I think the settle hook is appropriate for what you want to achieve. So something along these lines:

embla.on('settle', () => {
  /*
    Run the same code that scrolls the carousel slowly.
    You probably want to extract it into a reusable function.
  */
})

Please let me know if you have additional requirements or questions πŸ™‚.

Kindly, David

openscript commented 3 years ago

Hi again @davidjerleke

Thank you for taking the time and helping me out.

I've tried to implement it for a while, but I get very strange side-effects. I thought a video of the screen would be the easiest to show it:

https://drive.google.com/file/d/1rBKMEgZJFVjHT49NW9YL5_2PpxtwU3BJ/view

My code looks like this:

import styled from '@emotion/styled';
import { useEmblaCarousel } from 'embla-carousel/react';
import { EmblaCarousel } from 'embla-carousel/vanilla';
import { Engine } from 'embla-carousel/vanilla/components/engine';
import React, { useEffect } from 'react';

const Viewport = styled.section`
  overflow: hidden;
`;

const Container = styled.div`
  display: flex;
`;

const Slide = styled.div`
  position: relative;
  min-width: 100%;
`;

export type TickerProps = {};

export function Ticker() {
  const [tickerRef, ticker] = useEmblaCarousel({ loop: true, dragFree: true, containScroll: 'trimSnaps' });

  const startScrolling = (engine: Engine, ticker: EmblaCarousel) => {
    engine.scrollBody.useSpeed(0.01);
    engine.scrollTo.index((ticker.scrollSnapList().length || 0) - 1, -10);
  };

  useEffect(() => {
    const engine = ticker?.dangerouslyGetEngine();

    if (engine && ticker) {
      ticker?.on('settle', () => startScrolling(engine, ticker));

      startScrolling(engine, ticker);
    }
  }, [ticker]);

  return (
    <Viewport ref={tickerRef}>
      <Container>
        <Slide>111111111111</Slide>
        <Slide>222222222222</Slide>
        <Slide>333333333333</Slide>
      </Container>
    </Viewport>
  );
}

The direction parameter I set to -10 at engine.scrollTo.index I've chosen because with -1 the 'error' is barely visible. An observation I have made is, that the last slide (3) is getting slower and slower the closer it comes to the left end.

It is reproducible with this. It seems like the original has the same problem without my modifications. If you wait until it comes to the end, it is also getting slower and slower.

Greetings to the north, Robin

davidjerleke commented 3 years ago

Hello Robin (@openscript),

Side note: I took the liberty to modify your latest comment, only to add syntax highlight to your code. I hope that was ok?

Thank you for the additional information. Seems like I might be misunderstanding you.

It seems like the original has the same problem without my modifications. If you wait until it comes to the end, it is also getting slower and slower.

The requirements mentioned in issue #105 was to show users that the content is scrollable. So the idea was to start scrolling automatically from the beginning and stop scrolling at the end of the content. The easing at the beginning and the end was not an issue there.

Let's go through your use case and requirements again. Is this what you're looking for?

Feel free to provide an example of what you want to achieve (share a link to a website or similar).

Thanks in advance, David

openscript commented 3 years ago

Hello David (@davidjerleke)

It made me happy to see your kind reply. Thank you!

Yes, I think we are talking about the same use cases. Easing is not required, but it would maybe be nice for the speed up at the beginning and after dragging. Easing at the end doesn't make sense to me, if it's an endless loop.

I want to achieve something like on the Gatsby website, but with dragging: image

Have a great day,

Robin

davidjerleke commented 3 years ago

Hi Robin (@openscript),

Thanks for the example link. That helps πŸ™‚. I'll see if I can create a CodeSandbox for you, and I'll let you know how it goes when I've had the chance to do so.

Best, David

davidjerleke commented 3 years ago

Hello again Robin (@openscript),

I've created a CodeSandbox for you. This sandbox basically re-creates the internal Embla animation loop without the easing. I haven't had the time to test this thoroughly so I'm going to leave that up to you. I hope that's ok.

Please let me know if it's working as expected.

Kindly, David

openscript commented 3 years ago

Hi David (@davidjerleke)

Thank you so much! This is exactly what I wanted to achieve. It's pretty amazing that embla allows to configure this from outside.

Have a great day

Robin

filiptvarek commented 3 years ago

Hello, this solution isn't working since version 4.4.0. Loop does not working properly. Is here any solution for 4.5.3 please? To reproduce it you can only swap version in your CodeSandobox or my modified version with 4.5.3 and faster scroll.

davidjerleke commented 3 years ago

Hi @filiptvarek,

Thanks for noticing this. It stopped working because I refactored some internal methods in later versions. I've updated the original CodeSandbox in this comment and it should work as expected now.

Cheers, David

filiptvarek commented 3 years ago

It works. Thank you very much

easydrops commented 2 years ago

@ameliagroen @samdhoffman As for VanillaJS, I am fiddling around with this myself. Adapting the sandbox above, this seems to be working for me. But tbh I am not very familiar with the requestAnimationFrame API. So I don't know if this might lead to any drawbacks.


let rafId = 0;

const animate = () => {
  const engine = embla.dangerouslyGetEngine();
  engine.location.add(-0.025); // controls the speed
  engine.target.set(engine.location);
  engine.scrollLooper.loop(-1);
  engine.slideLooper.loop();
  engine.translate.to(engine.location);
  rafId = requestAnimationFrame(animate)
}
const startAutoScroll = () => {
 rafId = requestAnimationFrame(animate)
}
const stopAutoScroll = () => {
  rafId = cancelAnimationFrame(rafId) || 0;
}
embla.on("settle", startAutoScroll);
embla.on("pointerDown", stopAutoScroll);
startAutoScroll();    

Hope this helps :)

7iomka commented 11 months ago

It is a pity that there is no official example that would be supported when the next update version is released.

davidjerleke commented 11 months ago

Hi @7iomka,

I don't doubt that you mean well, but I don't think it's helping. I'm the sole maintainer of this project and always have an insane backlog. This is what's been done for the upcoming v8.

If devs really want me to add more features/plugins, and if I'm to spend even more of my unpaid spare time than I already do, I have to be motivated by something. I want this comment to be constructive so here are ways to not just say I want this an that, but also ways to actually support this project:

More support means better chances for me to add more examples, more features and plugins.

Best, David

davidjerleke commented 10 months ago

@Poylar, @dash-james, @AmeerSapadmi, @7iomka, @pedrobernardina, @abdulrauf11, @ameliagroen, @ruie, @samdhoffman:

There is a plugin being developed for this purpose that will be named embla-carousel-auto-scroll which at the time of writing is 80% complete. I'm hoping to be able to finish it soon:

davidjerleke commented 8 months ago

Hi @Poylar, @dash-james, @AmeerSapadmi, @7iomka, @pedrobernardina, @abdulrauf11, @ameliagroen, @ruie, @samdhoffman,Β @easydrops, @filiptvarek, @MHase, @ashtonlance, @dan-wu-open, @geenious, @ridwanrsk, @mdrahiem:

I'm very close to wrapping up this plugin. If anyone would like to help out and test it before the release, I would very much appreciate that. Maybe we can catch one or two bugs before the release and speed up the process.

If you're interested, I'm going to create CodeSandboxes that I will share with you here so let me know what setup you would prefer:

Thanks in advance!

mdrahiem commented 8 months ago

Hey @davidjerleke I would prefer React + Typescript. Also may I know what test scenarios you want me to cover? I have never done this before πŸ˜… but I would love to do it 😬 . Please guide me. TIA.

Poylar commented 8 months ago

hi, i would prefer react + js

davidjerleke commented 8 months ago

@Poylar and @mdrahiem thank you for considering testing this plugin. CodeSandboxes here:

React + JavaScript

React + TypeScript

Also may I know what test scenarios you want me to cover?

Please feel free to test it anyway you want. For example, you can read what the different options do below and test and see if they behave as expected. I will temporarily just dump the documentation page for the AutoScroll plugin below:


Documentation here

mdrahiem commented 8 months ago

Hello @davidjerleke I did test options and methods and below are the results.

Options: βœ… speed βœ… startDelay βœ… direction βœ… playOnInit βœ… stopOnInteraction βœ… stopOnMouseEnter βœ… stopOnFocusIn ❓ rootNode

Methods: ❌ play βœ… stop ❌ reset ❌ isPlaying

Events: ❓autoScroll:play ❓autoScroll:stop

βœ… - Success ❓- Not sure how to test ❌ - Not working (I think)

For isPlaying I tried the below code.

     <div>
        status:{' '}
        {emblaApi?.plugins().autoScroll?.isPlaying()
          ? 'playing'
          : 'not playing'}
      </div>

For play, reset I tried with below code.

  const stopPlay = useCallback(
    (index) => {
      if (!emblaApi) return
      emblaApi.plugins().autoScroll?.stop()
      emblaApi.scrollTo(index)
    },
    [emblaApi]
  )

  const resumePlay = useCallback(
    (index) => {
      if (!emblaApi) return
      emblaApi.plugins().autoScroll?.play()
      emblaApi.scrollTo(index)
    },
    [emblaApi]
  )

  const resetPlay = useCallback(
    (index) => {
      if (!emblaApi) return
      emblaApi.plugins().autoScroll?.reset()
      emblaApi.scrollTo(index)
    },
    [emblaApi]
  )

      <button onClick={stopPlay}>Stop</button>
      <button onClick={resumePlay}>Resume</button>
      <button onClick={resetPlay}>Reset</button>

Please correct me if my above implementation is wrong for play, reset, isPlaying and I would be glad to test Events if I find some example. Thanks!

davidjerleke commented 8 months ago

Thanks a lot for testing @mdrahiem!

I will update the CodeSandbox with the playing state, events and demonstrate how to do it. Stay tuned πŸ‘.

mdrahiem commented 8 months ago

Also an update on browsers.

βœ… - Chromium Engine Version 120 βœ… - Firefox 121.0 βœ… - Safari Version 17.2.1

davidjerleke commented 8 months ago

@mdrahiem please check the CodeSandbox again, I've added the features you weren't sure about so you can see how it's intended to work.

Best, David

mdrahiem commented 8 months ago

Thanks for updating the code @davidjerleke Below is the update on testing.

Methods: βœ… play βœ… stop βœ… isPlaying

Events: βœ… autoScroll:play βœ… autoScroll:stop

❌ reset - I was expecting the slider to be reset (start from slide 1) but what I see is, it stops for a while (because of startDelay) and then it plays from the same position. Please correct if my expectation is wrong.

❓ rootNode - I still couldn't find any implementation for this. I see the implementation in javascript version though. πŸ˜„

davidjerleke commented 8 months ago

@mdrahiem thank you for testing again πŸ™‚!

❌ reset - I was expecting the slider to be reset (start from slide 1) but what I see is, it stops for a while (because of startDelay) and then it plays from the same position. Please correct if my expectation is wrong.

I think I need to update the description of what this does. That's valuable input, thanks πŸ‘. The reset method does the following:

It's useful when you're using previous/next buttons and want the carousel to continue after the user has navigated with the buttons and the carousel has settled. See here and here.


❓ rootNode - I still couldn't find any implementation for this. I see the implementation in javascript version though. πŸ˜„

This allows you to select a custom element that should listen for mouseenter and mouseleave, if you don't want the default Embla root node (which is the element you attach emblaRef to). It's used when stopOnMouseEnter is true.


Did these explanations make sense?

Best, David

mdrahiem commented 8 months ago

Thank you so much for taking time and explaining these. They makes sense now πŸ™‚

davidjerleke commented 8 months ago

@mdrahiem thanks for testing ⭐! The plugin will be released soon.

mdrahiem commented 8 months ago

Glad I could be of some help. Please feel free to tell me if I can contribute more in anyway. πŸ™‚

davidjerleke commented 8 months ago

Thanks that's very kind of you @mdrahiem. @mdrahiem and anyone else reading this for that matter:

If you want to make contributions you can browse discussions with the label help wanted. We are happy to accept contributions from our users. For more details see the contributing guidelines.

After your pull request have been merged, you will automatically be added as a contributor here and here.

Best, David

davidjerleke commented 7 months ago

Update 2024-01-30 πŸŽ‰

embla-carousel-auto-scroll has been released to the NPM registry!

Update 2024-01-29

The person in question unpublished their package but the NPM registry threw errors when I tried to publish embla-carousel-auto-scroll. I've contacted NPM support and they said that the package wasn't properly unpublished so they did that for me. Unfortunately, NPM say that I have to wait 24h before I can try publishing embla-carousel-auto-scroll again.

Update 2024-01-26

Sorry guys, was just about to publish this package (embla-carousel-auto-scroll), but someone has taken a too similar name so I've asked them to unpublish their package before I can release the official package: