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 { isValidElement } from '../_util/props-util';
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 {
OptionsType as SelectOptionsType,
@ -153,8 +153,14 @@ const OptionList = defineComponent<OptionListProps, { state?: any }>({
scrollIntoView(index);
}
});
// Force trigger scrollbar visible when open
if (props.open) {
nextTick(()=>{
listRef.current?.scrollTo(undefined);
})
}
},
{ immediate: true, flush: 'post' },
{ immediate: true },
);
// ========================== Values ==========================

@ -84,16 +84,13 @@ const List = defineComponent({
},
setup(props) {
// ================================= MISC =================================
const useVirtual = computed(()=>{
const { height, itemHeight, virtual } = props;
return !!(virtual !== false && height && itemHeight);
})
const inVirtual = computed(() => {
const { height, itemHeight, data, virtual } = props;
return !!(
virtual !== false &&
height &&
itemHeight &&
data &&
itemHeight * data.length > height
);
const { height, itemHeight, data } = props;
return useVirtual.value && data && itemHeight * data.length > height;
});
const state = reactive<ListState>({
@ -103,7 +100,8 @@ const List = defineComponent({
});
const componentRef = ref<HTMLDivElement>();
const fillerInnerRef = ref<HTMLDivElement>();
const scrollBarRef = ref<any>(); // Hack on scrollbar to enable flash call
// =============================== Item Key ===============================
const getKey = (item: Record<string, any>) => {
if (typeof props.itemKey === 'function') {
@ -135,10 +133,9 @@ const List = defineComponent({
// ================================ Height ================================
const [setInstance, collectHeight, heights, heightUpdatedMark] = useHeights(getKey, null, null);
// ========================== Visible Calculation =========================
const calRes = computed(() => {
if (!inVirtual.value) {
if (!useVirtual.value) {
return {
scrollHeight: undefined,
start: 0,
@ -146,6 +143,17 @@ const List = defineComponent({
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 startIndex: number | undefined;
let startOffset: number | undefined;
@ -228,7 +236,7 @@ const List = defineComponent({
// Since this added in global,should use ref to keep update
const [onRawWheel, onFireFoxScroll] = useFrameWheel(
inVirtual,
useVirtual,
isScrollAtTop,
isScrollAtBottom,
offsetY => {
@ -240,7 +248,7 @@ const List = defineComponent({
);
// Mobile touch move
useMobileTouchMove(inVirtual, componentRef, (deltaY, smoothOffset) => {
useMobileTouchMove(useVirtual, componentRef, (deltaY, smoothOffset) => {
if (originScroll(deltaY, smoothOffset)) {
return false;
}
@ -250,7 +258,7 @@ const List = defineComponent({
});
// Firefox only
function onMozMousePixelScroll(e: MouseEvent) {
if (inVirtual.value) {
if (useVirtual.value) {
e.preventDefault();
}
}
@ -285,6 +293,9 @@ const List = defineComponent({
getKey,
collectHeight,
syncScrollTop,
() => {
scrollBarRef.value?.delayHidden();
},
);
const componentStyle = computed(() => {
@ -292,7 +303,7 @@ const List = defineComponent({
if (props.height) {
cs = { [props.fullHeight ? 'height' : 'maxHeight']: props.height + 'px', ...ScrollStyle };
if (inVirtual.value) {
if (useVirtual.value) {
cs!.overflowY = 'hidden';
if (state.scrollMoving) {
@ -310,11 +321,13 @@ const List = defineComponent({
onFallbackScroll,
onScrollBar,
componentRef,
inVirtual,
useVirtual,
calRes,
collectHeight,
setInstance,
sharedConfig,
scrollBarRef,
fillerInnerRef
};
},
render() {
@ -341,7 +354,7 @@ const List = defineComponent({
componentStyle,
onFallbackScroll,
onScrollBar,
inVirtual,
useVirtual,
collectHeight,
sharedConfig,
setInstance,
@ -375,13 +388,15 @@ const List = defineComponent({
height={scrollHeight}
offset={offset}
onInnerResize={collectHeight}
ref="fillerInnerRef"
>
{listChildren}
</Filler>
</Component>
{inVirtual && (
{useVirtual && (
<ScrollBar
ref="scrollBarRef"
prefixCls={prefixCls}
scrollTop={scrollTop}
height={height}

@ -137,7 +137,7 @@ export default defineComponent({
const enableScrollRange = this.getEnableScrollRange();
const enableHeightRange = this.getEnableHeightRange();
const ptg = newTop / enableHeightRange;
const ptg = enableHeightRange ? newTop / enableHeightRange : 0;
const newScrollTop = Math.ceil(ptg * enableScrollRange);
this.moveRaf = raf(() => {
onScroll(newScrollTop);
@ -164,30 +164,45 @@ export default defineComponent({
getEnableScrollRange() {
const { scrollHeight, height } = this.$props;
return scrollHeight - height;
return scrollHeight - height || 0;
},
getEnableHeightRange() {
const { height } = this.$props;
const spinHeight = this.getSpinHeight();
return height - spinHeight;
return height - spinHeight || 0;
},
getTop() {
const { scrollTop } = this.$props;
const enableScrollRange = this.getEnableScrollRange();
const enableHeightRange = this.getEnableHeightRange();
if (scrollTop === 0 || enableScrollRange === 0) {
return 0;
}
const ptg = scrollTop / enableScrollRange;
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() {
// eslint-disable-next-line no-unused-vars
const { visible, dragging } = this.state;
const { dragging } = this.state;
const { prefixCls } = this.$props;
const spinHeight = this.getSpinHeight() + 'px';
const top = this.getTop() + 'px';
const visible = this.getVisible();
return (
<div
ref={this.scrollbarRef}

@ -12,10 +12,18 @@ export default function useScrollTo(
getKey: GetKey,
collectHeight: () => void,
syncScrollTop: (newTop: number) => void,
triggerFlash: () => void,
) {
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!);
const data = state.mergedData;
const itemHeight = props.itemHeight;

Loading…
Cancel
Save