From 333998b518ec0ad562df4af683fa629c7ceed6eb Mon Sep 17 00:00:00 2001 From: wyc001122 <498040880@qq.com> Date: Tue, 15 Apr 2025 20:51:38 +0800 Subject: [PATCH] fix: determine if scrollbar has been totally scrolled (#5934) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 修复在系统屏幕缩放比例不为100%的情况下,滚动组件对是否已滚动到边界的判断可能不正确的问题 --- .../src/components/scrollbar/scrollbar.vue | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/scrollbar/scrollbar.vue b/packages/@core/ui-kit/shadcn-ui/src/components/scrollbar/scrollbar.vue index f066b91a..e101ba59 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/components/scrollbar/scrollbar.vue +++ b/packages/@core/ui-kit/shadcn-ui/src/components/scrollbar/scrollbar.vue @@ -39,6 +39,14 @@ const isAtRight = ref(false); const isAtBottom = ref(false); const isAtLeft = ref(true); +/** + * We have to check if the scroll amount is close enough to some threshold in order to + * more accurately calculate arrivedState. This is because scrollTop/scrollLeft are non-rounded + * numbers, while scrollHeight/scrollWidth and clientHeight/clientWidth are rounded. + * https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#determine_if_an_element_has_been_totally_scrolled + */ +const ARRIVED_STATE_THRESHOLD_PIXELS = 1; + const showShadowTop = computed(() => props.shadow && props.shadowTop); const showShadowBottom = computed(() => props.shadow && props.shadowBottom); const showShadowLeft = computed(() => props.shadow && props.shadowLeft); @@ -60,14 +68,18 @@ function handleScroll(event: Event) { const target = event.target as HTMLElement; const scrollTop = target?.scrollTop ?? 0; const scrollLeft = target?.scrollLeft ?? 0; - const offsetHeight = target?.offsetHeight ?? 0; - const offsetWidth = target?.offsetWidth ?? 0; + const clientHeight = target?.clientHeight ?? 0; + const clientWidth = target?.clientWidth ?? 0; const scrollHeight = target?.scrollHeight ?? 0; const scrollWidth = target?.scrollWidth ?? 0; isAtTop.value = scrollTop <= 0; isAtLeft.value = scrollLeft <= 0; - isAtBottom.value = scrollTop + offsetHeight >= scrollHeight; - isAtRight.value = scrollLeft + offsetWidth >= scrollWidth; + isAtBottom.value = + Math.abs(scrollTop) + clientHeight >= + scrollHeight - ARRIVED_STATE_THRESHOLD_PIXELS; + isAtRight.value = + Math.abs(scrollLeft) + clientWidth >= + scrollWidth - ARRIVED_STATE_THRESHOLD_PIXELS; emit('scrollAt', { bottom: isAtBottom.value,