perf(grid): optimize virtual scroll to reduce white flash during fast scrolling
Add buffer, skip-hover, will-change, and CSS contain properties to all RecycleScroller instances (DataGrid, ConnectionTree, ObjectBrowser, RedisKeyBrowser). Disable pointer-events during scroll in DataGrid to reduce repaint overhead.
This commit is contained in:
parent
368fa5a102
commit
78184544cf
|
|
@ -755,6 +755,17 @@ function syncHeaderScroll(e: Event) {
|
|||
}
|
||||
}
|
||||
|
||||
let scrollingTimer = 0;
|
||||
const isScrolling = ref(false);
|
||||
function onScrollerScroll(e: Event) {
|
||||
syncHeaderScroll(e);
|
||||
if (!isScrolling.value) isScrolling.value = true;
|
||||
clearTimeout(scrollingTimer);
|
||||
scrollingTimer = window.setTimeout(() => {
|
||||
isScrolling.value = false;
|
||||
}, 120);
|
||||
}
|
||||
|
||||
initColumnWidths();
|
||||
watch(() => visibleColumns.value.length, initColumnWidths);
|
||||
watch(
|
||||
|
|
@ -2134,10 +2145,13 @@ defineExpose({
|
|||
v-else
|
||||
ref="scrollerRef"
|
||||
class="data-grid-scroller flex-1 overflow-x-auto overscroll-none"
|
||||
:class="{ 'is-scrolling': isScrolling }"
|
||||
:items="displayItems"
|
||||
:item-size="26"
|
||||
:buffer="600"
|
||||
:skip-hover="true"
|
||||
key-field="id"
|
||||
@scroll="syncHeaderScroll"
|
||||
@scroll="onScrollerScroll"
|
||||
>
|
||||
<template #default="{ item, index }">
|
||||
<div
|
||||
|
|
@ -2600,6 +2614,8 @@ defineExpose({
|
|||
<style scoped>
|
||||
.data-grid-scroller {
|
||||
overflow-anchor: none;
|
||||
will-change: scroll-position;
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
.data-grid-scroller :deep(.vue-recycle-scroller__item-wrapper) {
|
||||
|
|
@ -2607,6 +2623,14 @@ defineExpose({
|
|||
overflow: visible;
|
||||
}
|
||||
|
||||
.data-grid-scroller :deep(.vue-recycle-scroller__item-view) {
|
||||
contain: layout style paint;
|
||||
}
|
||||
|
||||
.data-grid-scroller.is-scrolling :deep(.vue-recycle-scroller__item-view) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ddl-drawer-resizing {
|
||||
transition: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -408,7 +408,14 @@ watch(
|
|||
<div class="truncate">{{ t("objects.schemaColumn") }}</div>
|
||||
<div v-if="hasComments" class="truncate">{{ t("objects.comment") }}</div>
|
||||
</div>
|
||||
<RecycleScroller class="min-h-0 flex-1" :items="filteredRows" :item-size="38" key-field="id">
|
||||
<RecycleScroller
|
||||
class="object-browser-scroller min-h-0 flex-1"
|
||||
:items="filteredRows"
|
||||
:item-size="38"
|
||||
:buffer="600"
|
||||
:skip-hover="true"
|
||||
key-field="id"
|
||||
>
|
||||
<template #default="{ item }">
|
||||
<div
|
||||
class="grid h-[38px] cursor-pointer items-center gap-3 border-b px-3 hover:bg-accent/50"
|
||||
|
|
@ -514,3 +521,14 @@ watch(
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.object-browser-scroller {
|
||||
will-change: scroll-position;
|
||||
contain: content;
|
||||
}
|
||||
|
||||
.object-browser-scroller :deep(.vue-recycle-scroller__item-view) {
|
||||
contain: layout style paint;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -398,7 +398,15 @@ onMounted(loadKeys);
|
|||
<Loader2 class="w-3.5 h-3.5 animate-spin" />
|
||||
<span>{{ t("redis.loadingKeys") }}</span>
|
||||
</div>
|
||||
<RecycleScroller v-else class="flex-1" :items="visibleRows" :item-size="28" key-field="id">
|
||||
<RecycleScroller
|
||||
v-else
|
||||
class="redis-key-scroller flex-1"
|
||||
:items="visibleRows"
|
||||
:item-size="28"
|
||||
:buffer="600"
|
||||
:skip-hover="true"
|
||||
key-field="id"
|
||||
>
|
||||
<template #default="{ item: row }">
|
||||
<div
|
||||
class="grid border-b text-xs cursor-pointer hover:bg-accent/50 group"
|
||||
|
|
@ -500,3 +508,14 @@ onMounted(loadKeys);
|
|||
@confirm="applyDangerAction"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.redis-key-scroller {
|
||||
will-change: scroll-position;
|
||||
contain: content;
|
||||
}
|
||||
|
||||
.redis-key-scroller :deep(.vue-recycle-scroller__item-view) {
|
||||
contain: layout style paint;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -247,11 +247,12 @@ function onSearchToggle(node: TreeNode) {
|
|||
</div>
|
||||
<RecycleScroller
|
||||
v-if="flatNodes.length > 0 && useVirtualTree"
|
||||
class="min-h-0 flex-1 overflow-y-auto"
|
||||
class="connection-tree-scroller min-h-0 flex-1 overflow-y-auto"
|
||||
:items="flatNodes"
|
||||
:item-size="SIDEBAR_TREE_ROW_HEIGHT"
|
||||
:buffer="SIDEBAR_TREE_SCROLL_BUFFER"
|
||||
:prerender="SIDEBAR_TREE_PRERENDER_COUNT"
|
||||
:skip-hover="true"
|
||||
key-field="id"
|
||||
type-field="type"
|
||||
flow-mode
|
||||
|
|
@ -284,3 +285,14 @@ function onSearchToggle(node: TreeNode) {
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.connection-tree-scroller {
|
||||
will-change: scroll-position;
|
||||
contain: content;
|
||||
}
|
||||
|
||||
.connection-tree-scroller :deep(.vue-recycle-scroller__item-view) {
|
||||
contain: layout style paint;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { TreeNode, TreeNodeType } from "@/types/database";
|
||||
|
||||
export const SIDEBAR_TREE_ROW_HEIGHT = 28;
|
||||
export const SIDEBAR_TREE_SCROLL_BUFFER = SIDEBAR_TREE_ROW_HEIGHT * 48;
|
||||
export const SIDEBAR_TREE_SCROLL_BUFFER = 600;
|
||||
export const SIDEBAR_TREE_PRERENDER_COUNT = 48;
|
||||
|
||||
export interface FlatTreeNode {
|
||||
|
|
|
|||
Loading…
Reference in New Issue