perf: improve tabs-view scrolling (#4164)

This commit is contained in:
Vben
2024-08-15 23:30:07 +08:00
committed by GitHub
parent eb280ffeb7
commit d71a20ad0a
9 changed files with 107 additions and 57 deletions

View File

@@ -75,7 +75,7 @@ function scrollIntoView() {
id="tabs-scrollbar"
class="tabs-chrome__scrollbar h-full"
horizontal
scroll-bar-class="z-10"
scroll-bar-class="z-10 hidden"
>
<!-- footer -> 4px -->
<div

View File

@@ -72,12 +72,12 @@ function scrollIntoView() {
</script>
<template>
<div class="h-full flex-1 overflow-hidden">
<div class="size-full flex-1 overflow-hidden">
<VbenScrollbar
id="tabs-scrollbar"
class="tabs-scrollbar h-full"
horizontal
scroll-bar-class="z-10"
scroll-bar-class="z-10 hidden"
>
<div
:class="contentClass"

View File

@@ -7,8 +7,10 @@ import type { TabsProps } from './types';
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
import { useForwardPropsEmits, useSortable } from '@vben-core/composables';
import { ChevronLeft, ChevronRight } from '@vben-core/icons';
import { Tabs, TabsChrome } from './components';
import { useTabsViewScroll } from './use-tabs-view-scroll';
interface Props extends TabsProps {}
@@ -30,6 +32,8 @@ const emit = defineEmits<{
const forward = useForwardPropsEmits(props, emit);
const { initScrollbar, scrollDirection } = useTabsViewScroll();
const sortableInstance = ref<null | Sortable>(null);
// 可能会找到拖拽的子元素这里需要确保拖拽的dom时tab元素
@@ -104,13 +108,21 @@ async function initTabsSortable() {
sortableInstance.value = await initializeSortable();
}
onMounted(initTabsSortable);
async function init() {
await nextTick();
initTabsSortable();
initScrollbar();
}
onMounted(() => {
init();
});
watch(
() => props.styleType,
() => {
sortableInstance.value?.destroy();
initTabsSortable();
init();
},
);
@@ -120,6 +132,32 @@ onUnmounted(() => {
</script>
<template>
<TabsChrome v-if="styleType === 'chrome'" v-bind="forward" />
<Tabs v-else v-bind="forward" />
<div
:class="{
'overflow-hidden': styleType !== 'chrome',
}"
class="flex h-full flex-1"
>
<!-- 左侧滚动按钮 -->
<span
class="hover:bg-muted text-muted-foreground cursor-pointer border-r px-2"
@click="scrollDirection('left')"
>
<ChevronLeft class="size-4 h-full" />
</span>
<TabsChrome
v-if="styleType === 'chrome'"
v-bind="{ ...forward, ...$attrs, ...$props }"
/>
<Tabs v-else v-bind="{ ...forward, ...$attrs, ...$props }" />
<!-- 左侧滚动按钮 -->
<span
class="hover:bg-muted text-muted-foreground cursor-pointer border-l px-2"
@click="scrollDirection('right')"
>
<ChevronRight class="size-4 h-full" />
</span>
</div>
</template>

View File

@@ -0,0 +1,59 @@
import { nextTick, ref } from 'vue';
type El = Element | null | undefined;
export function useTabsViewScroll(scrollDistance: number = 150) {
const scrollbarEl = ref<El>(null);
const scrollViewportEl = ref<El>(null);
function getScrollClientWidth() {
if (!scrollbarEl.value || !scrollViewportEl.value) return {};
const scrollbarWidth = scrollbarEl.value.clientWidth;
const scrollViewWidth = scrollViewportEl.value.clientWidth;
return {
scrollbarWidth,
scrollViewWidth,
};
}
function scrollDirection(
direction: 'left' | 'right',
distance: number = scrollDistance,
) {
const { scrollbarWidth, scrollViewWidth } = getScrollClientWidth();
if (!scrollbarWidth || !scrollViewWidth) return;
if (scrollbarWidth > scrollViewWidth) return;
scrollViewportEl.value?.scrollBy({
behavior: 'smooth',
left:
direction === 'left'
? -(scrollbarWidth - distance)
: +(scrollbarWidth - distance),
});
}
async function initScrollbar() {
await nextTick();
const barEl = document.querySelector('#tabs-scrollbar');
const viewportEl = barEl?.querySelector(
'div[data-radix-scroll-area-viewport]',
);
scrollbarEl.value = barEl;
scrollViewportEl.value = viewportEl;
const activeItem = viewportEl?.querySelector('.is-active');
activeItem?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
return {
initScrollbar,
scrollDirection,
};
}