Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic and slow scroll with dragFree #105

Closed
flayks opened this issue Sep 18, 2020 · 27 comments
Closed

Automatic and slow scroll with dragFree #105

flayks opened this issue Sep 18, 2020 · 27 comments
Labels
duplicate This issue or pull request already exists

Comments

@flayks
Copy link

flayks commented Sep 18, 2020

I was wondering if there is a way to slowly auto scroll the content of a carousel with the dragFree option? In order to sort of show to users that there is more content in a nice way.

Kind of something like that, but automatically:

autoscroll

@davidjerleke
Copy link
Owner

davidjerleke commented Sep 20, 2020

Hello Félix (@flayks),

Thank you for your question. How about this CodeSandbox? Does it meet your requirements?

Best,
David

@flayks
Copy link
Author

flayks commented Sep 20, 2020

@davidjerleke, you are a code angel! It absolutely does what I had in mind. Thank you 🙌

@davidjerleke
Copy link
Owner

Great news @flayks! I'm closing this as resolved then. Enjoy 🙂.

@davidjerleke davidjerleke added question Question about how to achieve something resolved This issue is resolved labels Sep 20, 2020
@flayks
Copy link
Author

flayks commented Sep 21, 2020

Alright @davidjerleke, I spoke too soon.

I'm facing some issues with this feature, I'm using Next. Related to the code you made as well for the breakpoint checking, this throws me back a 500 error. Not much detail tho, unfortunately :/
If you have any idea? Have I done something wrong in my component logic or code?

import React, { useState, useEffect, useCallback } from 'react'
import { useEmblaCarousel } from 'embla-carousel/react'
import { debounce } from '../../utils/functions'


const Scroller = ({ children, screenThreshold = 549, settingsProps = {} }) => {
    const [isActive, setIsActive] = useState(false)
    const [scrollProgress, setScrollProgress] = useState(0)
    const [scroller, embla] = useEmblaCarousel({
        // Settings
        containScroll: 'trimSnaps',
        dragFree: true,
        ...settingsProps
    })


    // Check
    const checkEmbla = useCallback(() => {
        // Enable only on a small screen
        const smallScreen = document.documentElement.clientWidth < screenThreshold
        setIsActive(smallScreen)

        if (isActive) {
            // Scroll event
            onScroll()
            embla.on('scroll', onScroll)

            // Auto scroll slowly
            const engine = embla.dangerouslyGetEngine()
            engine.scrollBody.useSpeed(0.01)
            engine.scrollTo.index(embla.scrollSnapList().length - 1)
        }
    }, [embla, setIsActive])


    // On scroll
    const onScroll = useCallback(() => {
        if (!embla) return

        // Update scrollbar when scrolling
        setScrollProgress(Math.max(0, Math.min(1, embla.scrollProgress())) * 100)
    }, [embla, setScrollProgress])


    // Init
    useEffect(() => {
        // Check for carousel
        checkEmbla()

        // Resize
        window.addEventListener('resize', debounce(checkEmbla, 150))
    }, [checkEmbla])


    return isActive ? (
        <div className="scroller">
            <div className="scroller__container grid" ref={isActive ? scroller : null}>
                <div className="scroller__pan">
                    {children.map((child, index) => (
                        <div className="scroller__item" key={index}>
                            {child}
                        </div>
                    ))}
                </div>
            </div>

            <div className="scroller__progress">
                <div style={{ transform: `translateX(${scrollProgress}%)` }} />
            </div>
        </div>
    ) : children
}

export default Scroller

@flayks
Copy link
Author

flayks commented Sep 21, 2020

I suspect that the problem is coming from this line:

engine.scrollBody.useSpeed(0.01)

Commenting it indeed takes the Scroller component to the last item with no dramas, but as soon as I set the scrollBody.useSpeed method, I get the error. Mysterious 🤔

@davidjerleke
Copy link
Owner

Hi @flayks,

Just guessing but maybe you should perform a check if the window object exists? It won’t be there on the server right?

Best,
David

@flayks
Copy link
Author

flayks commented Sep 21, 2020

@davidjerleke that is true. Does engine.scrollBody.useSpeed() uses window or document?

@davidjerleke
Copy link
Owner

davidjerleke commented Sep 21, 2020

@flayks, I’m not sure because I don’t have access to my computer right now. Try document?

I guess Embla will try to scroll a container which should be an HTML element, but doesn’t exist on the server.

But to be honest I’m not sure if that’s what’s causing this.

@davidjerleke
Copy link
Owner

davidjerleke commented Sep 21, 2020

@flayks I noticed that your checkEmbla function doesn’t check for the existence of the embla variable before executing the scrolling? It only checks for isActive?

@flayks
Copy link
Author

flayks commented Sep 21, 2020

@davidjerleke I'll give it a try.

I tried to add it at the first place, but as soon as I do that:

const checkEmbla = useCallback(() => {
        if (!embla) return
...

It stops any kind of code after and breaks the detection with the breakpoint.

@davidjerleke
Copy link
Owner

davidjerleke commented Sep 21, 2020

@flayks the if statement should probably be inside isActive and wrap the auto scrolling code.

But also guessing at this point.

@flayks
Copy link
Author

flayks commented Sep 21, 2020

@davidjerleke right. It works in the isActive indeed, thanks!
Although, no luck for checking for window or document for engine.scrollBody.useSpeed().

@davidjerleke
Copy link
Owner

davidjerleke commented Sep 21, 2020

@flayks Maybe the window you use for the resize handler and the document for clientWidth need checks if they exist so they don’t cause 500 on the server side (unrelated to Embla)?

If this doesn’t solve the issue, I need a more detailed error log in order to debug this. Because I’m not sure Embla is causing the issue.

@flayks
Copy link
Author

flayks commented Sep 21, 2020

@davidjerleke I think it's alright as the whole code is ran in an useEffect method which is not rendered by Next. It runs pretty smoothly except for the useSpeed function

@davidjerleke
Copy link
Owner

davidjerleke commented Sep 21, 2020

@flayks I see. Is the issue present on the browser or server? Sorry but I still don’t have much useful information and a good understanding of the issue.

@flayks
Copy link
Author

flayks commented Sep 21, 2020

@davidjerleke ok, I've done some investigating and with a fresh NextJS app and only the Scroller component with some divs inside, and it works. It must be from somewhere else in my app, can't tell where but I'll have a deeper look. The code works tho! 👌

@flayks
Copy link
Author

flayks commented Sep 21, 2020

Okay @davidjerleke, I've got some news, and it's (I suppose), not Embla, but Preact. I configured my NextJS project using Preact instead of React, and there is something in the useSpeed that it doesn't like. What exactly is the question because even with the debugger/profiler I have close to 0 log, but that's the reason behind this confusion!

@davidjerleke
Copy link
Owner

Thanks for the update @flayks. So do you intend to use Embla with React or Preact? Or was the Preact setup a mistake?

Sounds strange, but I only have experience with React, not Preact, so I can’t even start guessing why this is happening. Especially without an error log.

@flayks
Copy link
Author

flayks commented Sep 22, 2020

@davidjerleke I started the project using Next with React but I tried to use Preact in order to reduce the bundle size, but I feel that it's causing more issues than giving a lightweight code haha
I created a repo replicating my setup: https://github.com/flayks/embla-next-preact
As I mentioned, hardly any errors or logging, it only works without the line 35 of the Scroller.jsx file.
Not a huge deal IMO, but that's a bit odd.

@davidjerleke
Copy link
Owner

davidjerleke commented Sep 22, 2020

Thanks for clarifying Félix (@flayks). That sounds a bit odd yeah. With no intention of being rude, at the time of writing Embla Carousel works with Vanilla-JS and React. I haven't tested Embla with Preact at all. I might decide to add support in the future and in that case the repo you shared may be helpful for debugging purposes 👍.

I'm glad it works with React though 🙂.

Best,
David

@flayks
Copy link
Author

flayks commented Sep 22, 2020

@davidjerleke no worries. I just found that weird. As I don't know much about Preact either I just wanted to give it a go, but no big deal really, I switched back to React instead. That would be good for the peeps using it tho, as it's a super good lightweight alternative to React. Thanks for your help anyway!

@NickWoodward
Copy link

NickWoodward commented Sep 8, 2023

just stumbled across this and wondered if this is an alright approach to the same problem, or am I abusing the duration and delay settings?

  const autoplayOptions = {  delay:0,  rootNode: (emblaRoot: HTMLElement) => emblaRoot.parentElement}
  const options = { loop: true, duration: 8000 }
  
  const [emblaRef, emblaApi] = useEmblaCarousel(options, [Autoplay(autoplayOptions)])

@davidjerleke
Copy link
Owner

@raymondarevalo no need to look for temporary CodeSandbox solutions anymore. A plugin for this is finished and will be released very soon. Stay tuned.

Here’s the PR for it:

@davidjerleke davidjerleke closed this as not planned Won't fix, can't repro, duplicate, stale Jan 25, 2024
@davidjerleke
Copy link
Owner

@alisayed95 no example sandbox yet but the plugin has been released.

@raymondarevalo
Copy link

@davidjerleke random question, but would you have any reference on how 'embla.dangerouslyGetEngine()' was used before this new plugin?

I'm currently doing this

    const engine = this.carousel.internalEngine();
    engine.location.add(this.speed);
    engine.target.set(engine.location);
    engine.scrollLooper.loop(this.speed);
    engine.slideLooper.loop();
    engine.translate.to(engine.location);

But then realized I should probably be using 'dangerouslyGetEngine()'

@davidjerleke
Copy link
Owner

@raymondarevalo what do you want to achieve? If it's unrelated this issue. Please ask for help here.

Repository owner deleted a comment from alisayed95 Feb 19, 2024
Repository owner deleted a comment from raymondarevalo Feb 19, 2024
@raymondarevalo
Copy link

@davidjerleke Oh yeah, it's related to this issue. Basically I wanted to create an infinite loop using a previous method that was being used on this thread. However, I noticed that the engine.location wasn't being reset after an entire loop and therefore embla.scrollProgress() was going beyond 1.

`
loop() {
if (this.paused) return;
const engine = this.carousel.internalEngine();

engine.location.add(this.speed);

engine.target.set(engine.location);

engine.scrollLooper.loop(this.speed);
engine.slideLooper.loop();
engine.translate.to(engine.location);

console.log("slide scroll progress", this.carousel.scrollProgress());

const that = this;
this.raf = window.requestAnimationFrame(() => that.loop());

}
`

I'm currently using 'embla 7.1'.

Last note, I tried using the new plugin, but realized it broke whenever I resized the screen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
duplicate This issue or pull request already exists
Projects
None yet
Development

No branches or pull requests

4 participants