perf: update virtual-list
parent
a3a060077b
commit
2e61e9cb50
|
@ -5,7 +5,7 @@ import classNames from '../_util/classNames';
|
||||||
import pickAttrs from '../_util/pickAttrs';
|
import pickAttrs from '../_util/pickAttrs';
|
||||||
import { isValidElement } from '../_util/props-util';
|
import { isValidElement } from '../_util/props-util';
|
||||||
import createRef from '../_util/createRef';
|
import createRef from '../_util/createRef';
|
||||||
import { computed, defineComponent, reactive, VNodeChild, watch } from 'vue';
|
import { computed, defineComponent, nextTick, reactive, VNodeChild, watch } from 'vue';
|
||||||
import List from '../vc-virtual-list/List';
|
import List from '../vc-virtual-list/List';
|
||||||
import {
|
import {
|
||||||
OptionsType as SelectOptionsType,
|
OptionsType as SelectOptionsType,
|
||||||
|
@ -153,8 +153,14 @@ const OptionList = defineComponent<OptionListProps, { state?: any }>({
|
||||||
scrollIntoView(index);
|
scrollIntoView(index);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// Force trigger scrollbar visible when open
|
||||||
|
if (props.open) {
|
||||||
|
nextTick(()=>{
|
||||||
|
listRef.current?.scrollTo(undefined);
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true, flush: 'post' },
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
// ========================== Values ==========================
|
// ========================== Values ==========================
|
||||||
|
|
|
@ -84,16 +84,13 @@ const List = defineComponent({
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
// ================================= MISC =================================
|
// ================================= MISC =================================
|
||||||
|
const useVirtual = computed(()=>{
|
||||||
|
const { height, itemHeight, virtual } = props;
|
||||||
|
return !!(virtual !== false && height && itemHeight);
|
||||||
|
})
|
||||||
const inVirtual = computed(() => {
|
const inVirtual = computed(() => {
|
||||||
const { height, itemHeight, data, virtual } = props;
|
const { height, itemHeight, data } = props;
|
||||||
return !!(
|
return useVirtual.value && data && itemHeight * data.length > height;
|
||||||
virtual !== false &&
|
|
||||||
height &&
|
|
||||||
itemHeight &&
|
|
||||||
data &&
|
|
||||||
itemHeight * data.length > height
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const state = reactive<ListState>({
|
const state = reactive<ListState>({
|
||||||
|
@ -103,7 +100,8 @@ const List = defineComponent({
|
||||||
});
|
});
|
||||||
|
|
||||||
const componentRef = ref<HTMLDivElement>();
|
const componentRef = ref<HTMLDivElement>();
|
||||||
|
const fillerInnerRef = ref<HTMLDivElement>();
|
||||||
|
const scrollBarRef = ref<any>(); // Hack on scrollbar to enable flash call
|
||||||
// =============================== Item Key ===============================
|
// =============================== Item Key ===============================
|
||||||
const getKey = (item: Record<string, any>) => {
|
const getKey = (item: Record<string, any>) => {
|
||||||
if (typeof props.itemKey === 'function') {
|
if (typeof props.itemKey === 'function') {
|
||||||
|
@ -135,10 +133,9 @@ const List = defineComponent({
|
||||||
|
|
||||||
// ================================ Height ================================
|
// ================================ Height ================================
|
||||||
const [setInstance, collectHeight, heights, heightUpdatedMark] = useHeights(getKey, null, null);
|
const [setInstance, collectHeight, heights, heightUpdatedMark] = useHeights(getKey, null, null);
|
||||||
|
|
||||||
// ========================== Visible Calculation =========================
|
// ========================== Visible Calculation =========================
|
||||||
const calRes = computed(() => {
|
const calRes = computed(() => {
|
||||||
if (!inVirtual.value) {
|
if (!useVirtual.value) {
|
||||||
return {
|
return {
|
||||||
scrollHeight: undefined,
|
scrollHeight: undefined,
|
||||||
start: 0,
|
start: 0,
|
||||||
|
@ -146,6 +143,17 @@ const List = defineComponent({
|
||||||
offset: undefined,
|
offset: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always use virtual scroll bar in avoid shaking
|
||||||
|
if (!inVirtual.value) {
|
||||||
|
return {
|
||||||
|
scrollHeight: fillerInnerRef.value?.offsetHeight || 0,
|
||||||
|
start: 0,
|
||||||
|
end: state.mergedData.length - 1,
|
||||||
|
offset: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let itemTop = 0;
|
let itemTop = 0;
|
||||||
let startIndex: number | undefined;
|
let startIndex: number | undefined;
|
||||||
let startOffset: number | undefined;
|
let startOffset: number | undefined;
|
||||||
|
@ -228,7 +236,7 @@ const List = defineComponent({
|
||||||
|
|
||||||
// Since this added in global,should use ref to keep update
|
// Since this added in global,should use ref to keep update
|
||||||
const [onRawWheel, onFireFoxScroll] = useFrameWheel(
|
const [onRawWheel, onFireFoxScroll] = useFrameWheel(
|
||||||
inVirtual,
|
useVirtual,
|
||||||
isScrollAtTop,
|
isScrollAtTop,
|
||||||
isScrollAtBottom,
|
isScrollAtBottom,
|
||||||
offsetY => {
|
offsetY => {
|
||||||
|
@ -240,7 +248,7 @@ const List = defineComponent({
|
||||||
);
|
);
|
||||||
|
|
||||||
// Mobile touch move
|
// Mobile touch move
|
||||||
useMobileTouchMove(inVirtual, componentRef, (deltaY, smoothOffset) => {
|
useMobileTouchMove(useVirtual, componentRef, (deltaY, smoothOffset) => {
|
||||||
if (originScroll(deltaY, smoothOffset)) {
|
if (originScroll(deltaY, smoothOffset)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -250,7 +258,7 @@ const List = defineComponent({
|
||||||
});
|
});
|
||||||
// Firefox only
|
// Firefox only
|
||||||
function onMozMousePixelScroll(e: MouseEvent) {
|
function onMozMousePixelScroll(e: MouseEvent) {
|
||||||
if (inVirtual.value) {
|
if (useVirtual.value) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -285,6 +293,9 @@ const List = defineComponent({
|
||||||
getKey,
|
getKey,
|
||||||
collectHeight,
|
collectHeight,
|
||||||
syncScrollTop,
|
syncScrollTop,
|
||||||
|
() => {
|
||||||
|
scrollBarRef.value?.delayHidden();
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const componentStyle = computed(() => {
|
const componentStyle = computed(() => {
|
||||||
|
@ -292,7 +303,7 @@ const List = defineComponent({
|
||||||
if (props.height) {
|
if (props.height) {
|
||||||
cs = { [props.fullHeight ? 'height' : 'maxHeight']: props.height + 'px', ...ScrollStyle };
|
cs = { [props.fullHeight ? 'height' : 'maxHeight']: props.height + 'px', ...ScrollStyle };
|
||||||
|
|
||||||
if (inVirtual.value) {
|
if (useVirtual.value) {
|
||||||
cs!.overflowY = 'hidden';
|
cs!.overflowY = 'hidden';
|
||||||
|
|
||||||
if (state.scrollMoving) {
|
if (state.scrollMoving) {
|
||||||
|
@ -310,11 +321,13 @@ const List = defineComponent({
|
||||||
onFallbackScroll,
|
onFallbackScroll,
|
||||||
onScrollBar,
|
onScrollBar,
|
||||||
componentRef,
|
componentRef,
|
||||||
inVirtual,
|
useVirtual,
|
||||||
calRes,
|
calRes,
|
||||||
collectHeight,
|
collectHeight,
|
||||||
setInstance,
|
setInstance,
|
||||||
sharedConfig,
|
sharedConfig,
|
||||||
|
scrollBarRef,
|
||||||
|
fillerInnerRef
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
|
@ -341,7 +354,7 @@ const List = defineComponent({
|
||||||
componentStyle,
|
componentStyle,
|
||||||
onFallbackScroll,
|
onFallbackScroll,
|
||||||
onScrollBar,
|
onScrollBar,
|
||||||
inVirtual,
|
useVirtual,
|
||||||
collectHeight,
|
collectHeight,
|
||||||
sharedConfig,
|
sharedConfig,
|
||||||
setInstance,
|
setInstance,
|
||||||
|
@ -375,13 +388,15 @@ const List = defineComponent({
|
||||||
height={scrollHeight}
|
height={scrollHeight}
|
||||||
offset={offset}
|
offset={offset}
|
||||||
onInnerResize={collectHeight}
|
onInnerResize={collectHeight}
|
||||||
|
ref="fillerInnerRef"
|
||||||
>
|
>
|
||||||
{listChildren}
|
{listChildren}
|
||||||
</Filler>
|
</Filler>
|
||||||
</Component>
|
</Component>
|
||||||
|
|
||||||
{inVirtual && (
|
{useVirtual && (
|
||||||
<ScrollBar
|
<ScrollBar
|
||||||
|
ref="scrollBarRef"
|
||||||
prefixCls={prefixCls}
|
prefixCls={prefixCls}
|
||||||
scrollTop={scrollTop}
|
scrollTop={scrollTop}
|
||||||
height={height}
|
height={height}
|
||||||
|
|
|
@ -137,7 +137,7 @@ export default defineComponent({
|
||||||
const enableScrollRange = this.getEnableScrollRange();
|
const enableScrollRange = this.getEnableScrollRange();
|
||||||
const enableHeightRange = this.getEnableHeightRange();
|
const enableHeightRange = this.getEnableHeightRange();
|
||||||
|
|
||||||
const ptg = newTop / enableHeightRange;
|
const ptg = enableHeightRange ? newTop / enableHeightRange : 0;
|
||||||
const newScrollTop = Math.ceil(ptg * enableScrollRange);
|
const newScrollTop = Math.ceil(ptg * enableScrollRange);
|
||||||
this.moveRaf = raf(() => {
|
this.moveRaf = raf(() => {
|
||||||
onScroll(newScrollTop);
|
onScroll(newScrollTop);
|
||||||
|
@ -164,30 +164,45 @@ export default defineComponent({
|
||||||
|
|
||||||
getEnableScrollRange() {
|
getEnableScrollRange() {
|
||||||
const { scrollHeight, height } = this.$props;
|
const { scrollHeight, height } = this.$props;
|
||||||
return scrollHeight - height;
|
return scrollHeight - height || 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
getEnableHeightRange() {
|
getEnableHeightRange() {
|
||||||
const { height } = this.$props;
|
const { height } = this.$props;
|
||||||
const spinHeight = this.getSpinHeight();
|
const spinHeight = this.getSpinHeight();
|
||||||
return height - spinHeight;
|
return height - spinHeight || 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
getTop() {
|
getTop() {
|
||||||
const { scrollTop } = this.$props;
|
const { scrollTop } = this.$props;
|
||||||
const enableScrollRange = this.getEnableScrollRange();
|
const enableScrollRange = this.getEnableScrollRange();
|
||||||
const enableHeightRange = this.getEnableHeightRange();
|
const enableHeightRange = this.getEnableHeightRange();
|
||||||
|
if (scrollTop === 0 || enableScrollRange === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
const ptg = scrollTop / enableScrollRange;
|
const ptg = scrollTop / enableScrollRange;
|
||||||
return ptg * enableHeightRange;
|
return ptg * enableHeightRange;
|
||||||
},
|
},
|
||||||
|
// Not show scrollbar when height is large thane scrollHeight
|
||||||
|
getVisible () {
|
||||||
|
const { visible } = this.state;
|
||||||
|
const { height, scrollHeight } = this.$props;
|
||||||
|
|
||||||
|
if (height >= scrollHeight) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return visible;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
const { visible, dragging } = this.state;
|
const { dragging } = this.state;
|
||||||
const { prefixCls } = this.$props;
|
const { prefixCls } = this.$props;
|
||||||
const spinHeight = this.getSpinHeight() + 'px';
|
const spinHeight = this.getSpinHeight() + 'px';
|
||||||
const top = this.getTop() + 'px';
|
const top = this.getTop() + 'px';
|
||||||
|
const visible = this.getVisible();
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={this.scrollbarRef}
|
ref={this.scrollbarRef}
|
||||||
|
|
|
@ -12,10 +12,18 @@ export default function useScrollTo(
|
||||||
getKey: GetKey,
|
getKey: GetKey,
|
||||||
collectHeight: () => void,
|
collectHeight: () => void,
|
||||||
syncScrollTop: (newTop: number) => void,
|
syncScrollTop: (newTop: number) => void,
|
||||||
|
triggerFlash: () => void,
|
||||||
) {
|
) {
|
||||||
let scroll: number | null = null;
|
let scroll: number | null = null;
|
||||||
|
|
||||||
return (arg: any) => {
|
return (arg?: any) => {
|
||||||
|
// When not argument provided, we think dev may want to show the scrollbar
|
||||||
|
if (arg === null || arg === undefined) {
|
||||||
|
triggerFlash();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal scroll logic
|
||||||
raf.cancel(scroll!);
|
raf.cancel(scroll!);
|
||||||
const data = state.mergedData;
|
const data = state.mergedData;
|
||||||
const itemHeight = props.itemHeight;
|
const itemHeight = props.itemHeight;
|
||||||
|
|
Loading…
Reference in New Issue