Skip to content

Commit

Permalink
perf: update virtual-list
Browse files Browse the repository at this point in the history
  • Loading branch information
tangjinzhou committed Nov 4, 2020
1 parent a3a0600 commit 2e61e9c
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 26 deletions.
10 changes: 8 additions & 2 deletions components/vc-select/OptionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 ==========================
Expand Down
53 changes: 34 additions & 19 deletions components/vc-virtual-list/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>({
Expand All @@ -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') {
Expand Down Expand Up @@ -135,17 +133,27 @@ 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,
end: state.mergedData.length - 1,
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;
Expand Down Expand Up @@ -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 => {
Expand All @@ -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;
}
Expand All @@ -250,7 +258,7 @@ const List = defineComponent({
});
// Firefox only
function onMozMousePixelScroll(e: MouseEvent) {
if (inVirtual.value) {
if (useVirtual.value) {
e.preventDefault();
}
}
Expand Down Expand Up @@ -285,14 +293,17 @@ const List = defineComponent({
getKey,
collectHeight,
syncScrollTop,
() => {
scrollBarRef.value?.delayHidden();
},
);

const componentStyle = computed(() => {
let cs: CSSProperties | null = null;
if (props.height) {
cs = { [props.fullHeight ? 'height' : 'maxHeight']: props.height + 'px', ...ScrollStyle };

if (inVirtual.value) {
if (useVirtual.value) {
cs!.overflowY = 'hidden';

if (state.scrollMoving) {
Expand All @@ -310,11 +321,13 @@ const List = defineComponent({
onFallbackScroll,
onScrollBar,
componentRef,
inVirtual,
useVirtual,
calRes,
collectHeight,
setInstance,
sharedConfig,
scrollBarRef,
fillerInnerRef
};
},
render() {
Expand All @@ -341,7 +354,7 @@ const List = defineComponent({
componentStyle,
onFallbackScroll,
onScrollBar,
inVirtual,
useVirtual,
collectHeight,
sharedConfig,
setInstance,
Expand Down Expand Up @@ -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}
Expand Down
23 changes: 19 additions & 4 deletions components/vc-virtual-list/ScrollBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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}
Expand Down
10 changes: 9 additions & 1 deletion components/vc-virtual-list/hooks/useScrollTo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 2e61e9c

Please sign in to comment.