-
-
Notifications
You must be signed in to change notification settings - Fork 837
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
Fix rendering performance bottleneck in carousel #2281
base: next
Are you sure you want to change the base?
Fix rendering performance bottleneck in carousel #2281
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
I did some light testing in chrome by using the override feature and it seemed to work alright, but I must warn, this will convert something that used to be synchronous to be async, so there might be some assumptions hiding somewhere which could be the source of bugs. Someone (and I could be willing to do this), should probably audit all of the callers of goToSlide to see if any of the code after it's called could possibly expect that the actual scrolling has been completed. Biggest likely offender is that state property |
@alenaksu any concerns before we merge this? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've checked the changes and I don't believe it will cause any issue. The pendingSlideChange
property is used to prevent slide synchronization when a slide change is triggered by user interaction, such as clicking a navigation dot, so it is not an issue in this case.
However, there could be a side effect causing multiple synchronizations during the initial render. I left a comment wwith a suggestion to overcome this, please take a look and let me know what you think.
const doWork = () => { | ||
const scrollContainer = this.scrollContainer; | ||
const scrollContainerRect = scrollContainer.getBoundingClientRect(); | ||
const nextSlideRect = slide.getBoundingClientRect(); | ||
|
||
const nextLeft = nextSlideRect.left - scrollContainerRect.left; | ||
const nextTop = nextSlideRect.top - scrollContainerRect.top; | ||
|
||
if (nextLeft || nextTop) { | ||
this.pendingSlideChange = true; | ||
scrollContainer.scrollTo({ | ||
left: nextLeft + scrollContainer.scrollLeft, | ||
top: nextTop + scrollContainer.scrollTop, | ||
behavior | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To keep the original behaviour of the pendingSlideChange
, we could set the value before the raf callback and then update it within if needed:
const doWork = () => { | |
const scrollContainer = this.scrollContainer; | |
const scrollContainerRect = scrollContainer.getBoundingClientRect(); | |
const nextSlideRect = slide.getBoundingClientRect(); | |
const nextLeft = nextSlideRect.left - scrollContainerRect.left; | |
const nextTop = nextSlideRect.top - scrollContainerRect.top; | |
if (nextLeft || nextTop) { | |
this.pendingSlideChange = true; | |
scrollContainer.scrollTo({ | |
left: nextLeft + scrollContainer.scrollLeft, | |
top: nextTop + scrollContainer.scrollTop, | |
behavior | |
}); | |
} | |
this.pendingSlideChange = true; | |
const doWork = () => { | |
const scrollContainer = this.scrollContainer; | |
const scrollContainerRect = scrollContainer.getBoundingClientRect(); | |
const nextSlideRect = slide.getBoundingClientRect(); | |
const nextLeft = nextSlideRect.left - scrollContainerRect.left; | |
const nextTop = nextSlideRect.top - scrollContainerRect.top; | |
if (nextLeft || nextTop) { | |
scrollContainer.scrollTo({ | |
left: nextLeft + scrollContainer.scrollLeft, | |
top: nextTop + scrollContainer.scrollTop, | |
behavior | |
}); | |
} else { | |
this.pendingSlideChange = false; | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would make pendingSlideChange=true more often than it is today.
Today it's only true if either the left or top is non zero, but here we would always be marking it has pending until the next frame.
I'm ok with that, but wanted to point out that it's also not a clean refactor / preservation of existing behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See 68be53c
if ('requestAnimationFrame' in window) { | ||
window.requestAnimationFrame(doWork); | ||
} else { | ||
doWork(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can avoid this check and call rAF directly since it's supported by all browsers
requestAnimationFrame(() => {
...
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, wasn't sure what minimum browser support was, so played it cautious. I can clean this up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see dea63b3
this might also fix #2296 since the render making scrollContainer exist will happen before the rAF callback does |
Fixes #2280
Before
After
Notice how none of the carousels on the right side have any synchronous Layout anymore, but the browser does one big layout once in the requestAnimationFrame callback.
NOTE: The scales on these screenshots are not the same...but the key point to notice is there there aren't as many tiny red dog-ears on each of the individual carousels which is the concern I was trying to fix.