Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 46 additions & 18 deletions core/src/components/segment/segment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,13 @@ export class Segment implements ComponentInterface {
* before we can scroll.
*/
raf(() => {
this.scrollActiveButtonIntoView();
/**
* When the segment loads for the first
* time we just want to snap the active button into
* place instead of scroll. Smooth scrolling should only
* happen when the user interacts with the segment.
*/
this.scrollActiveButtonIntoView(false);
});

this.gesture = (await import('../../utils/gesture')).createGesture({
Expand Down Expand Up @@ -305,30 +311,52 @@ export class Segment implements ComponentInterface {
}
}

private scrollActiveButtonIntoView() {
const { scrollable, value } = this;
private scrollActiveButtonIntoView(smoothScroll = true) {
const { scrollable, value, el } = this;

if (scrollable) {
const buttons = this.getButtons();
const activeButton = buttons.find((button) => button.value === value);
if (activeButton !== undefined) {
const scrollContainerBox = el.getBoundingClientRect();
const activeButtonBox = activeButton.getBoundingClientRect();

/**
* Subtract the active button x position from the scroll
* container x position. This will give us the x position
* of the active button within the scroll container.
*/
const activeButtonLeft = activeButtonBox.x - scrollContainerBox.x;

/**
* If we just used activeButtonLeft, then the active button
* would be aligned with the left edge of the scroll container.
* Instead, we want the segment button to be centered. As a result,
* we subtract half of the scroll container width. This will position
* the left edge of the active button at the midpoint of the scroll container.
* We then add half of the active button width. This will position the active
* button such that the midpoint of the active button is at the midpoint of the
* scroll container.
*/
const centeredX = activeButtonLeft - scrollContainerBox.width / 2 + activeButtonBox.width / 2;

/**
* Scrollable segment buttons should be
* centered within the view including
* buttons that are partially offscreen.
* We intentionally use scrollBy 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
* we use this API instead.
*
* 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.
*/
activeButton.scrollIntoView({
behavior: 'smooth',
inline: 'center',

/**
* Segment should scroll on the
* horizontal axis. `block: 'nearest'`
* ensures that the vertical axis
* does not scroll if the segment
* as a whole is already in view.
*/
block: 'nearest',
el.scrollBy({
top: 0,
left: centeredX,
behavior: smoothScroll ? 'smooth' : 'instant',
});
}
}
Expand Down