Skip to content

Commit

Permalink
Prerender items longer for scroll direction
Browse files Browse the repository at this point in the history
  • Loading branch information
inokawa committed Jul 28, 2023
1 parent 11023a6 commit 28d1155
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 31 deletions.
3 changes: 2 additions & 1 deletion src/core/scroller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ACTION_SCROLL_END,
UPDATE_SIZE,
ACTION_MANUAL_SCROLL,
SCROLL_IDLE,
} from "./store";
import { debounce, throttle, max, min, timeout } from "./utils";

Expand All @@ -18,7 +19,7 @@ const createOnWheel = (
onScrollStopped: () => void
) => {
return throttle((e: WheelEvent) => {
if (!store._getIsScrolling()) {
if (store._getScrollDirection() === SCROLL_IDLE) {
// Scroll start should be detected with scroll event
return;
}
Expand Down
19 changes: 8 additions & 11 deletions src/core/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ type Subscriber = (sync?: boolean) => void;
export const UPDATE_SCROLL = 0b00001;
export const UPDATE_SIZE = 0b00010;
export const UPDATE_JUMP = 0b00100;
export const UPDATE_IS_SCROLLING = 0b01000;
export const UPDATE_SCROLL_DIRECTION = 0b01000;
export const UPDATE_SCROLL_WITH_EVENT = 0b10000;

export type VirtualStore = {
Expand All @@ -80,7 +80,7 @@ export type VirtualStore = {
_getItemIndexForScrollTo(offset: number): number;
_subscribe(target: number, cb: Subscriber): () => void;
_update(...action: Actions): void;
_getIsScrolling(): boolean;
_getScrollDirection(): ScrollDirection;
_updateCacheLength(length: number): void;
};

Expand All @@ -101,8 +101,8 @@ export const createVirtualStore = (
let jumpCount = 0;
let jump: ScrollJump = 0;
let _scrollDirection: ScrollDirection = SCROLL_IDLE;
let _isManualScrolling = false;
let _resized = false;
let _isManualScrolling = false;
let _prevRange: ItemsRange = [0, initialItemCount];

const subscribers = new Set<[number, Subscriber]>();
Expand All @@ -118,10 +118,7 @@ export const createVirtualStore = (
const updateScrollDirection = (dir: ScrollDirection): boolean => {
const prev = _scrollDirection;
_scrollDirection = dir;
if (
_scrollDirection !== prev &&
(_scrollDirection === SCROLL_IDLE || prev === SCROLL_IDLE)
) {
if (_scrollDirection !== prev) {
return true;
}
return false;
Expand Down Expand Up @@ -296,7 +293,7 @@ export const createVirtualStore = (
scrollOffset > payload ? SCROLL_UP : SCROLL_DOWN
)
) {
mutated += UPDATE_IS_SCROLLING;
mutated += UPDATE_SCROLL_DIRECTION;
}
}

Expand All @@ -316,7 +313,7 @@ export const createVirtualStore = (
}
case ACTION_SCROLL_END: {
if (updateScrollDirection(SCROLL_IDLE)) {
mutated = UPDATE_IS_SCROLLING;
mutated = UPDATE_SCROLL_DIRECTION;
}
_isManualScrolling = false;
break;
Expand All @@ -337,8 +334,8 @@ export const createVirtualStore = (
});
}
},
_getIsScrolling() {
return _scrollDirection !== SCROLL_IDLE;
_getScrollDirection() {
return _scrollDirection;
},
_updateCacheLength(length) {
// It's ok to be updated in render because states should be calculated consistently regardless cache length
Expand Down
36 changes: 28 additions & 8 deletions src/react/VGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import {
ReactNode,
useImperativeHandle,
} from "react";
import { VirtualStore, createVirtualStore } from "../core/store";
import {
SCROLL_DOWN,
SCROLL_IDLE,
SCROLL_UP,
VirtualStore,
createVirtualStore,
} from "../core/store";
import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect";
import { useSelector } from "./useSelector";
import { max, min, values } from "../core/utils";
Expand Down Expand Up @@ -278,13 +284,15 @@ export const VGrid = forwardRef<VGridHandle, VGridProps>(

const [startRowIndex, endRowIndex] = useSelector(vStore, vStore._getRange);
const [startColIndex, endColIndex] = useSelector(hStore, hStore._getRange);
const verticalScrolling = useSelector(vStore, vStore._getIsScrolling);
const horizontalScrolling = useSelector(hStore, hStore._getIsScrolling);
const vScrollDirection = useSelector(vStore, vStore._getScrollDirection);
const hScrollDirection = useSelector(hStore, hStore._getScrollDirection);
const vJumpCount = useSelector(vStore, vStore._getJumpCount);
const hJumpCount = useSelector(hStore, hStore._getJumpCount);
const height = useSelector(vStore, vStore._getCorrectedScrollSize, true);
const width = useSelector(hStore, hStore._getCorrectedScrollSize, true);
const rootRef = useRef<HTMLDivElement>(null);
const vScrolling = vScrollDirection !== SCROLL_IDLE;
const hScrolling = hScrollDirection !== SCROLL_IDLE;

useIsomorphicLayoutEffect(() => {
const root = rootRef[refKey]!;
Expand Down Expand Up @@ -358,10 +366,22 @@ export const VGrid = forwardRef<VGridHandle, VGridProps>(
};
}, [children]);

const overscanedStartRowIndex = max(startRowIndex - overscan, 0);
const overscanedEndRowIndex = min(endRowIndex + overscan, rowCount - 1);
const overscanedStartColIndex = max(startColIndex - overscan, 0);
const overscanedEndColIndex = min(endColIndex + overscan, colCount - 1);
const overscanedStartRowIndex = max(
startRowIndex - (vScrollDirection === SCROLL_DOWN ? 1 : max(1, overscan)),
0
);
const overscanedEndRowIndex = min(
endRowIndex + (vScrollDirection === SCROLL_UP ? 1 : max(1, overscan)),
rowCount - 1
);
const overscanedStartColIndex = max(
startColIndex - (hScrollDirection === SCROLL_DOWN ? 1 : max(1, overscan)),
0
);
const overscanedEndColIndex = min(
endColIndex + (hScrollDirection === SCROLL_UP ? 1 : max(1, overscan)),
colCount - 1
);
const items = useMemo(() => {
const res: ReactElement[] = [];
for (let i = overscanedStartRowIndex; i <= overscanedEndRowIndex; i++) {
Expand Down Expand Up @@ -396,7 +416,7 @@ export const VGrid = forwardRef<VGridHandle, VGridProps>(
ref={rootRef}
width={width}
height={height}
scrolling={verticalScrolling || horizontalScrolling}
scrolling={vScrolling || hScrolling}
attrs={useMemo(
() => ({
...windowAttrs,
Expand Down
21 changes: 17 additions & 4 deletions src/react/VList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ import {
ReactNode,
useEffect,
} from "react";
import { UPDATE_SCROLL_WITH_EVENT, createVirtualStore } from "../core/store";
import {
SCROLL_DOWN,
SCROLL_IDLE,
SCROLL_UP,
UPDATE_SCROLL_WITH_EVENT,
createVirtualStore,
} from "../core/store";
import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect";
import { useSelector } from "./useSelector";
import { exists, max, min, values } from "../core/utils";
Expand Down Expand Up @@ -196,10 +202,11 @@ export const VList = forwardRef<VListHandle, VListProps>(
store._updateCacheLength(count);

const [startIndex, endIndex] = useSelector(store, store._getRange);
const scrolling = useSelector(store, store._getIsScrolling);
const scrollDirection = useSelector(store, store._getScrollDirection);
const jumpCount = useSelector(store, store._getJumpCount);
const scrollSize = useSelector(store, store._getCorrectedScrollSize, true);
const rootRef = useRef<HTMLDivElement>(null);
const scrolling = scrollDirection !== SCROLL_IDLE;

useIsomorphicLayoutEffect(() => {
const root = rootRef[refKey]!;
Expand Down Expand Up @@ -264,8 +271,14 @@ export const VList = forwardRef<VListHandle, VListProps>(
[]
);

const overscanedStartIndex = max(startIndex - overscan, 0);
const overscanedEndIndex = min(endIndex + overscan, count - 1);
const overscanedStartIndex = max(
startIndex - (scrollDirection === SCROLL_DOWN ? 1 : max(1, overscan)),
0
);
const overscanedEndIndex = min(
endIndex + (scrollDirection === SCROLL_UP ? 1 : max(1, overscan)),
count - 1
);
const items = useMemo(() => {
const res: ReactElement[] = [];
for (let i = overscanedStartIndex; i <= overscanedEndIndex; i++) {
Expand Down
20 changes: 16 additions & 4 deletions src/react/WVList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ import {
forwardRef,
useImperativeHandle,
} from "react";
import { createVirtualStore } from "../core/store";
import {
SCROLL_DOWN,
SCROLL_IDLE,
SCROLL_UP,
createVirtualStore,
} from "../core/store";
import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect";
import { useSelector } from "./useSelector";
import { exists, max, min, values } from "../core/utils";
Expand Down Expand Up @@ -150,10 +155,11 @@ export const WVList = forwardRef<WVListHandle, WVListProps>(
store._updateCacheLength(count);

const [startIndex, endIndex] = useSelector(store, store._getRange);
const scrolling = useSelector(store, store._getIsScrolling);
const scrollDirection = useSelector(store, store._getScrollDirection);
const jumpCount = useSelector(store, store._getJumpCount);
const scrollSize = useSelector(store, store._getCorrectedScrollSize, true);
const rootRef = useRef<HTMLDivElement>(null);
const scrolling = scrollDirection !== SCROLL_IDLE;

useIsomorphicLayoutEffect(() => {
const root = rootRef[refKey]!;
Expand Down Expand Up @@ -200,8 +206,14 @@ export const WVList = forwardRef<WVListHandle, WVListProps>(
[]
);

const overscanedStartIndex = max(startIndex - overscan, 0);
const overscanedEndIndex = min(endIndex + overscan, count - 1);
const overscanedStartIndex = max(
startIndex - (scrollDirection === SCROLL_DOWN ? 1 : max(1, overscan)),
0
);
const overscanedEndIndex = min(
endIndex + (scrollDirection === SCROLL_UP ? 1 : max(1, overscan)),
count - 1
);
const items = useMemo(() => {
const res: ReactElement[] = [];
for (let i = overscanedStartIndex; i <= overscanedEndIndex; i++) {
Expand Down
6 changes: 3 additions & 3 deletions src/react/useSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { flushSync } from "react-dom";
import { useRefWithUpdate } from "./useRefWithUpdate";
import { refKey } from "./utils";
import {
UPDATE_IS_SCROLLING,
UPDATE_SCROLL_DIRECTION,
UPDATE_JUMP,
UPDATE_SCROLL,
UPDATE_SIZE,
Expand All @@ -26,8 +26,8 @@ export const useSelector = <T>(
target = UPDATE_SIZE;
} else if (getSnapShot === store._getJumpCount) {
target = UPDATE_JUMP;
} else if (getSnapShot === store._getIsScrolling) {
target = UPDATE_IS_SCROLLING;
} else if (getSnapShot === store._getScrollDirection) {
target = UPDATE_SCROLL_DIRECTION;
} else {
// Others will be item subscribers
target = UPDATE_SIZE;
Expand Down

0 comments on commit 28d1155

Please sign in to comment.