embla-carousel
embla-carousel copied to clipboard
[Bug]: iOS Stutter
Which variants of Embla Carousel are you using?
- [ ] embla-carousel (Core)
- [X] embla-carousel-react
- [ ] embla-carousel-vue
- [ ] embla-carousel-svelte
- [ ] embla-carousel-autoplay
- [ ] embla-carousel-auto-scroll
- [ ] embla-carousel-solid
- [ ] embla-carousel-auto-height
- [ ] embla-carousel-class-names
- [ ] embla-carousel-fade
- [ ] embla-carousel-docs (Documentation)
- [ ] embla-carousel-docs (Generator)
Steps to reproduce
The bug occurs when I use caoursel.scrollTo(index, false) to smoothly scroll to the selected item.
Expected Behavior
It should scroll smoothly on iOS browsers as it does as non-Safari browsers.
Additional Context
I believe it has something to do with Safari/webkit.
On Android Chrome, it is perfectly smooth. On MacOS Chrome, it is perfectly smooth. On MacOS Safari, it stutters. On iOS Safari, it stutters. On iOS Chrome (which is still WebKit), it stutters.
What browsers are you seeing the problem on?
Safari
Version
8.1.3
CodeSandbox
https://codesandbox.io/p/sandbox/silly-haze-3evud?file=%2Findex.html
Before submitting
- [X] I've made research efforts and searched the documentation
- [X] I've searched for existing issues
- [X] I agree to follow this project's Contributing Guidelines for bug reports
@WilliamWelsh thanks. While I appreciate the effort, I get issues or questions like this once in a while where the dev in question claims that the scroll animations stutters. It’s really hard for me to do anything about this when I don’t see any stuttering on Safari whether I test it on my iOS or OSX devices. Anything could cause this including a performance heavy implementation of a carousel on top of the Embla library.
Do you have anything else to add, something more substantial that could support your claim and/or help me reproduce this?
If you increase the duration prop, it's easier to notice. Chrome (excluding Chrome iOS) it feels like butter, but every WebKit device/browser I've tried (even on embla-carousel.com) I can notice stutter.
@WilliamWelsh have you tried well known CSS tricks to promote the container (the element that is scrolled with transform: translate) to a separate layer? Example:
.embla__container {
backface-visibility: hidden;
}
/* If that doesn’t work, try this instead (not both): */
.embla__container {
will-change: transform;
}
Does that affect the animation in any way?
It seems to be a lot better with the backface-visibility setting, combined with changing the duration to 10 the animation is so fast that you can't really notice it anymore
It seems to be a lot better with the backface-visibility setting, combined with changing the duration to 10 the animation is so fast that you can't really notice it anymore
@WilliamWelsh so basically no, it didn't help then. I've put together a little test with a simple scroll animation for both JS and CSS here:
If you wouldn't mind, test both of them on webkit and Chrome browsers respectively, and let me know the results. Which ones stutter if any?
Thanks in advance! David
Hi @davidjerleke It seems that when using css animation, in low power mode on iphone there is no stutter like with js animation.
@sarussss when did you check? Right now or a while ago?
@davidjerleke I just checked about 30 minutes ago
@sarussss sorry to ask you but could you try Test once more? Both default and low battery mode and see if there's stuttering going on? I made some changes to the test sandbox like 20 minutes ago.
@davidjerleke I just checked it. Default mode: There is no difference between css and js animation. Low battery mode: js animation is still a bit slower than css, but it seems to reduce stuttering.
@sarussss thanks. What device did you test on?
@davidjerleke I use Iphone 15 Pro to test.
@sarussss thanks! I also tested both on iPhone and MacBook Pro and I couldn’t reproduce any stutter when triggering the JS example, even with low power mode active. However, the JS animation is slightly longer/slower (but still not stuttering). When you say this:
Low battery mode: js animation is still a bit slower than css, but it seems to reduce stuttering.
Do you mean that the JS animation is slower only or also stuttering?
@davidjerleke I think is still stuttering, but the stuttering has improved. Video: https://youtube.com/shorts/anKVGukcSNQ?si=hEyE2H7-3Uohlyaz Demo: https://y682vc.csb.app/
@sarussss thanks for sharing the screen recording. Watching it, I think both stutter (which is expected and makes sense because it’s low battery mode after all), and it seems like the JS animation only stutters slightly more than the CSS animation (the difference is barely noticeable). Do you agree?
It's definitely noticeable by a lot of people, which is why I opened the issue
@WilliamWelsh, first off, the Test sandbox has changed since you were here the last time because I've updated it. The JS animation there doesn't use the same animation technique as the current version of Embla Carousel.
Secondly, @sarussss wrote this:
Default mode: There is no difference between css and js animation. Low battery mode: js animation is still a bit slower than css, but it seems to reduce stuttering.
My test results are aligned with what @sarussss wrote above, with the exception that I can barely even notice a difference between the CSS and JS animation when low battery mode is activated. But watching @sarussss screen recording, the CSS animation stutters too. So the difference isn't big by any means.
And the whole point of low power mode is to save battery. Even native apps can stutter. So if the CSS and JS animation is just as smooth in this Test when the battery is loaded, and there's just a slight difference of how much they stutter between the CSS and JS animation in low power mode, I think the approach could be a candidate which could make it to the main library.
@WilliamWelsh I don't understand how this is helping:
It's definitely noticeable by a lot of people, which is why I opened the issue
You didn't answer my following question before:
Do you have anything else to add, something more substantial that could support your claim and/or help me reproduce this?
As I mentioned, Test sandbox has changed since you were here the last time. The stuttering @sarussss mentioned is in low power mode so that doesn't even support your claim about stuttering on webkit browsers. At least not when the battery is loaded.
I'm maintaining this on my spare time so if you don't respect that and you don't have anything constructive to say just don't say anything? What do you mean when you say "a lot of people"? The whole point of this conversation is for me to outline what choices I have moving forward, and the pros and cons of the options I have to solve/improve the animation smoothness. The point is not to read repeating comments that doesn't add any value to this conversation.
Hi @davidjerleke Totally agree with you, hope you bring these improvements to Embla, so I can test on Embla when swiping, when swiping it will often be more jerky.
Thanks @sarussss!
I will definitely see how I can improve the animation engine. Thanks for taking the time to discuss, test and sharing useful information like screen recordings and similar.
After investigating this further, I was lucky and found that there was some iOS stutter on very specific carousel sizes and when a carousel had more than 8 slides which is weird. These two things got rid of the iOS stutter for me:
-
The internal interpolation math for the carousel animation was slightly off. It has been fixed in the following PR: #933. More specifically here in the code. The fix was released in
v8.1.7. -
This is something devs have to do themselves: Add the following CSS
transform: translate3d(0, 0, 0);to all slides. A fix for this was added to the docs in the following commit.
Hi @davidjerleke I just checked and the low battery jerking has not improved yet. Video recording doesn't seem to show errors as easily as live viewing. https://github.com/user-attachments/assets/88fd58b4-7b6e-46cf-8bb7-ce5af280b9e6
@sarussss I’ve also investigated the low battery/battery saving mode and this is my answer:
Embla has a JavaScript spring animation engine that handles the animations instead of simple CSS easings. This means that the animations are done with JavaScript and more specifically with requestAnimationFrame. There are numerous advantages with spring based animations and if you really want to understand the benefits of this design choice, I suggest you check these out:
- Spring animations talk by Apple developers
- This article that explains the difference between spring animations and CSS easings.
However, the main downside with JavaScript animations compared to CSS easing transitions is the degraded performance when low battery mode is active. This happens because unfortunately, iOS and some other devices throttle requestAnimationFrame heavily when low battery mode is active. The penalty is actually insane compared to CSS animations. But this is happening on a browser native level. I can’t do anything about it.
With that said, this doesn’t mean that CSS easing transitions should be preferred because of the low battery performance reduction that some browsers apply to JavaScript animations. I think the benefits of spring animations with JavaScript still outweigh simple CSS easings (please at least read the article to understand why). And when I built the Embla core, I didn’t want to build yet another carousel library with CSS easings like almost all other libraries use. They feel stiff like you’re moving around a refrigerator or a huge stone block. I wanted to build a robust spring based carousel library with animations that feel natural, with motion that is proportional to how vigorous the swipe gesture is.
I don’t really understand the design choice of JavaScript animation throttling and the reasons behind it. If the purpose is to save battery, I think it still doesn’t make much sense, because there are multiple ways to destroy the battery without using requestAnimationFrame. Just look at the LinkedIn and Instagram website: Both kill your battery in no time. Especially the LinkedIn website freezes as soon and you interact with it and drains your battery.
On the other hand, one could argue that the battery saving mode is called so because of a reason. Performance restrictions should be expected, both when using a browser and even native apps. But still, I think the level of performance restrictions are way too high. Low battery mode should still allow for a usable experience even if performance is degraded.
So if you or someone else reading this want to try do anything about this, please create a new WebKit bug and share the link so anyone else that sees this can go to the bug report and add a +1 to it to show that it’s frustrating for developers. If we’re lucky, the WebKit team might pick it up and decrease the requestAnimationFrame performance penalty when low battery mode is active.
Hi @davidjerleke Thanks for your answer.
Hey @davidjerleke
First of all sorry for digging up old thread, but I believe it’s the best place for it, given that the issue was not truly resolved. I started to use your carousel and lucky me ofc I have performance issues, even without the low battery mode, still trying to find out why but in the meantime I found this issue and yes - I strongly believe the spring animations are the way to go, yet I don’t relate to your conclusion that we need to use JS - well we don’t:
https://developer.chrome.com/docs/css-ui/css-linear-easing-function
https://github.com/jakearchibald/linear-easing-generator
https://linear-easing-generator.netlify.app/
I am using it with great success in my tailwind-css project for all my other spring animations, and believe me, there is a ton of them. So while I’m working on to find out my issue, I was hoping you could check it out, and maybe even implement it, so maybe, just maybe, my issue would be resolved automatically 😁
Cheers
@Forsect you will find an answer to your comment here:
- https://github.com/davidjerleke/embla-carousel/discussions/1141#discussioncomment-12448923
@Forsect Just to help narrow things down—what device are you noticing the stutter on?
@davidjerleke Sorry, didn't see the message :x Well, all of them. iPhone 16 Pro Max, Samsung Galaxy S23, some other android phones, MBP M3 MAX, all browsers.
@Forsect Could you please provide something more concrete that I can work with? A screen recording and a CodeSandbox setup where the issue occurs would be really helpful. Also, does the stutter happen when dragging, or when the animation is triggered by the navigation buttons, or both?
I have problems with iphone 11 ios 16.4 and some other models.
@JudithGo I’d really appreciate it if we could keep things concrete. It’s hard for me to help when claims are made without any supporting detail. If you’re experiencing stuttering and would like me to look into it, please share a CodeSandbox with a reproducible example of your setup so I have something to work with.
Thanks.