From 078ed0b86a0d8e9f8457481cb739ea214195adce Mon Sep 17 00:00:00 2001 From: Maria Hutt Date: Mon, 23 Sep 2024 10:33:55 -0700 Subject: [PATCH] fix(segment): prevent flickering for scrollable on iOS (#29884) Issue number: resolves #29523 --------- ## What is the current behavior? The scrollable segment flickers on iOS physical devices or simulators when the active button is near the edge of the screen. The jump is due to the button being scrolled to the center and snaps back to the edge since the button was scrolled past the container. ## What is the new behavior? - Switched to `scrollTo` provides for a smoother transition. - Gave co author credit to the original reporter since they provided part of the solution - No new tests were created since functionality stays the same and testing on Playwright would be impossible to recreate ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information Dev build: 8.3.2-dev.11726779768.16e1f1d2 How to test: 1. Create a new app through any starter 2. Add a scrollable segment with at least 6 buttons (code snippet example below) 3. Recommended to change the segment mode to `md` since it's easier to see the flicker 4. Build the app and open it in an iOS or simulator (if more instructions on how to do this is needed, reach out to me) 5. Click on the third button 6. Click on the first button 7. Notice the flicker 8. Click over to the third to last button 9. Click on either the last two buttons 10. Notice the flicker 11. Install the dev build 12. Verify the load does not flicker 13. Repeat steps 4 and 5 14. Verify the flicker is no longer there 15. Repeat steps 7 and 8 16. Verify the flicker is no longer there ```js Button 1 Button 2 Button 3 Button 4 Button 5 Button 6 ``` --------- Co-authored-by: rostislavcz <58735164+rostislavcz@users.noreply.github.com> --- core/src/components/segment/segment.tsx | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index 4c425e18b2e..3ddf8645896 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -342,21 +342,35 @@ export class Segment implements ComponentInterface { const centeredX = activeButtonLeft - scrollContainerBox.width / 2 + activeButtonBox.width / 2; /** - * We intentionally use scrollBy here instead of scrollIntoView + * newScrollPosition is the absolute scroll position that the + * container needs to move to in order to center the active button. + * It is calculated by adding the current scroll position + * (scrollLeft) to the offset needed to center the button + * (centeredX). + */ + const newScrollPosition = el.scrollLeft + centeredX; + + /** + * We intentionally use scrollTo here instead of scrollIntoView * to avoid a WebKit bug where accelerated animations break * when using scrollIntoView. Using scrollIntoView will cause the * segment container to jump during the transition and then snap into place. * This is because scrollIntoView can potentially cause parent element - * containers to also scroll. scrollBy does not have this same behavior, so + * containers to also scroll. scrollTo does not have this same behavior, so * we use this API instead. * + * scrollTo is used instead of scrollBy because there is a + * Webkit bug that causes scrollBy to not work smoothly when + * the active button is near the edge of the scroll container. + * This leads to the buttons to jump around during the transition. + * * Note that if there is not enough scrolling space to center the element * within the scroll container, the browser will attempt * to center by as much as it can. */ - el.scrollBy({ + el.scrollTo({ top: 0, - left: centeredX, + left: newScrollPosition, behavior: smoothScroll ? 'smooth' : 'instant', }); }