perf: update virtual-list

pull/3128/head
tanjinzhou 4 years ago
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…
Cancel
Save