// set scroll progress
// accepts a number from 0 - 1 representing % scrolled of scrollable distance
// scroll to target is smooth
embla.scrollToProgress(0.5)
As discussed in issue #21.
@xiel for this feature request.
Hi Felix (@xiel),
I'm currently investigating this feature and I'd like to discuss it further with you.
This feature mainly raises one concern for me. I recently implemented scrollBy(), and every time the user calls it Embla checks what the target snap index will be. This is not an expensive approach since scrollBy() is smooth and it doesn't make sense to call it very fast repeatedly.
But I imagine that setting scroll progress instantly means that users may use this feature to update it quite fast and repeatedly, by connecting their own alternative input or similar. This may lead to performance issues if Embla has to check the target snap index in an intense manner (like 100 times in a second or similar).
Any thoughts regarding this?
Kindly,
David
To be honest I think both methods should support being set frequently (every frame) & hard.
Calculating the snap point should not be a very expensive calculation when the layout & snap points stayed the same?
Thanks for your input Felix (@xiel),
Well yes, maybe it's not an expensive calculation. But there is another thing to consider. Setting this frequently (every frame) from let's say snap index 0 to 4 will trigger the select event four times which could be pretty annoying if you're listening to the select event:
Another question is how we should solve this when loop is enabled and scroll is smooth. Should the carousel seek the closest way to the target (given scroll progress), like how scrollTo() works? For example, if current scroll progress is 0.9 and the user calls embla.scrollToProgress(0.1), this would scroll forward instead of backward?
Thanks in advance,
David
1) The same would happen if I would drag it I assume? If I use the select listener to update a pagination or similar, I would want to update it as soon as the index changes, even during the drag/animation process. The dev can always debounce if he is only interested in less frequent updates I would say?
2) I would not make it scroll forward automatically when you call 0.1. Instead I would allow to set 1.1 (% 1) to set 0.1 forward if wanted by the dev in loop mode.
Thanks for your input Felix (@xiel). I'll consider your thoughts and input when implementing this feature. I'm looking into this so I'll let you know when I have something.
Best
David
Hello Felix (@xiel),
I'm excited to announce release v2.9.1 which comes with a new set of utilities to manipulate the current scroll progress. Please read the release description for further details 馃帀.
Just as an example, it's now possible for a carousel to imitate another carousels progress like so:
carouselOne.on('select', () => {
const target = true
const carouselOneProgressTarget = carouselOne.scrollProgress(target) // boolean true gets the target scroll progress instead of current scroll progress
carouselTwo.scrollToProgress(carouselOneProgressTarget)
}
I'd very much appreciate if you could confirm that it's working as expected.
Cheers!
David
Can you explain what is the difference between target and not target position in scrollProgress(true/false). Not very clear to me atm, when are the numbers different?
Everything looks great, thanks so much!
Thanks @xiel for the feedback. So the difference is:
Location progress:
Returns the carousels scroll progress for its current scroll location. For example, when the carousel has scrolled half its scrollable length, it will return 0.5.
Target progress:
Returns the carousels scroll progress for its destination. For example, if the user does something that sets the carousel in motion, like drags it or clicks on a navigation button, the carousel will start scrolling towards a target. This returns the progress to where the carousel is headed, and not where it's right now. So let's say the carousel is on the first snap, and the user clicks to navigate to the last snap, this will return 1 (which is max scroll progress) even though the carousel hasn't reached its target yet.
Is this explanation better? Let me know if this makes sense 馃檪.
Cheers!
@davidcetinkaya Ah yes, totally makes sense! Thanks!
What do you think would be the best way to update the scrollPos by px values now?
Me might also need an API to read the current scrollLength then?
For example, I know I want to move 20px from current position, how can I translate that to the percent I need for scrollBy(x). I would need to have 20 / scrollLength?
I think it would be more efficient if I was able to read that from embla, than reading layout myself.
Background
I'm currently working on a small lib that normalizes wheel events and does momentum detection for a better UX (inertia scolling in macOS/Win10), and I would like to basically add fine grained control over the embla carousel using wheel events of mouse and trackpad (#47).
As not everyone will want wheel events, I thought I might offer this as a wrapper/add-on/different package later on to you, so only people wanting it, would get the extra ~2kb needed.
Ah I see. I'm going to add the wheel scrolling feature to Embla as discussed in issue #47 as soon as possible. This will be an option and not mandatory. Will this help your case? Or am I misunderstanding you maybe?
I'm basically offering you, to basically take over implementing issue #47 ;)
But if you prefer to do it yourself that is fine too of course.
I understand! That would be awesome @xiel 馃檪.
I've had this "crazy" idea for a while now, about exposing the Embla engine with all its internal utils like so:
embla.dangerouslyGetEngine()
But I'm afraid the lib will drown in issues if I do this 馃檲. But maybe it's just enough to expose the internal pxToPercent() utility and percentToProgress() then?
For future extensibility exposing the full engine would surely be a bit more dangerous, but a good choice IMHO. I think it would be a great way to able to add functionality via plugins/wrappers which could be installed separately, so the main package stays small and tidy.
Thanks for the feedback @xiel. Appreciate your thoughts and efforts 馃憤. Do you think I should add the engine exposure to the README? Because doing so might lead to a lot of issues about how to achieve this and that. Or do you want me to just do it and send you the info?
I think everyone who wants to use the engine directly needs to dive a bit into the code anyway, so I don't think there is a need to add lot of documentation for it :)
Happy to get it via mail or it could be a small section in CONTRIBUTING.md or another extra file?
Thanks! I'll let you know when I have something.
Cheers!
David
Hi Felix (@xiel),
I'm preparing for the release of Embla v.3, but this might take a week or so to finish, so I've decided to commit and push the api method that exposes the engine, and I've done so in this commit. So if you clone the repo you should be able to make use of:
const engine = embla.dangerouslyGetEngine()
...now 馃檪. For anyone building an extension package for Embla I'd be very happy to help out with how the engine works. So feel free to ask questions if you're building the scroll wheel package.
With the engine exposed, you won't need scrollBy or scrollProgress at all to achieve what you want. Embla runs an animation loop which renders updates with requestAnimationFrame. And by updating its target and starting the loop, Embla will seek it's target with attraction physics. These methods should help you:
// Grab engine
const engine = embla.dangerouslyGetEngine()
// When wheel starts scrolling you can do this
engine.scrollBody.useSpeed(80) // This attraction is so much that it will almost move instantly
engine.animation.start()
// Update target so attraction kicks in and moves the carousel towards its target
const pxToScrollBy = wheelDeltaInPx
const percentToScrollBy = engine.pxToPercent(pxToScrollBy) // Convert px to percent
engine.target.add(percentToScrollBy)
Please take a closer look at src/components/dragHandler.ts. Maybe it will help you forward 馃槂.
Best of luck and let me know if you need further info!
Kindly
David
@davidcetinkaya Perfect thanks! I'll let you know when I have something to show or questions ;)