perf: do not animate outside visible range

This commit is contained in:
computerjazz 2022-08-02 09:06:19 -07:00
parent abf9105e1a
commit 355edf9ebe
4 changed files with 62 additions and 21 deletions

View File

@ -35,6 +35,11 @@ type RNGHFlatListProps<T> = Animated.AnimateProps<
}
>;
type OnViewableItemsChangedCallback<T> = Exclude<
FlatListProps<T>["onViewableItemsChanged"],
undefined | null
>;
const AnimatedFlatList = (Animated.createAnimatedComponent(
FlatList
) as unknown) as <T>(props: RNGHFlatListProps<T>) => React.ReactElement;
@ -62,6 +67,8 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
autoScrollDistance,
panGestureState,
isTouchActiveNative,
viewableIndexMin,
viewableIndexMax,
disabled,
} = useAnimatedValues();
@ -287,6 +294,21 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
useAutoScroll();
const onViewableItemsChanged = useStableCallback<
OnViewableItemsChangedCallback<T>
>((info) => {
const viewableIndices = info.viewableItems
.filter((item) => item.isViewable)
.map((item) => item.index)
.filter((index): index is number => typeof index === "number");
const min = Math.min(...viewableIndices);
const max = Math.max(...viewableIndices);
viewableIndexMin.value = min;
viewableIndexMax.value = max;
props.onViewableItemsChanged?.(info);
});
return (
<DraggableFlatListProvider
activeKey={activeKey}
@ -305,6 +327,7 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
<AnimatedFlatList
{...props}
data={props.data}
onViewableItemsChanged={onViewableItemsChanged}
CellRendererComponent={CellRendererComponent}
ref={flatlistRef}
onContentSizeChange={onListContentSizeChange}

View File

@ -37,7 +37,7 @@ export function useAnimatedValues() {
function useSetupAnimatedValues<T>() {
const props = useProps<T>();
const DEFAULT_VAL = useSharedValue(0)
const DEFAULT_VAL = useSharedValue(0);
const containerSize = useSharedValue(0);
const scrollViewSize = useSharedValue(0);
@ -63,18 +63,25 @@ function useSetupAnimatedValues<T>() {
const scrollOffset = useSharedValue(0);
const scrollInit = useSharedValue(0);
const viewableIndexMin = useSharedValue(0);
const viewableIndexMax = useSharedValue(0);
// If list is nested there may be an outer scrollview
const outerScrollOffset = props.outerScrollOffset || DEFAULT_VAL;
const outerScrollInit = useSharedValue(0);
useAnimatedReaction(() => {
return activeIndexAnim.value
}, (cur, prev) => {
useAnimatedReaction(
() => {
return activeIndexAnim.value;
},
(cur, prev) => {
if (cur !== prev && cur >= 0) {
scrollInit.value = scrollOffset.value;
outerScrollInit.value = outerScrollOffset.value;
}
}, [outerScrollOffset]);
},
[outerScrollOffset]
);
const placeholderOffset = useSharedValue(0);
@ -83,10 +90,10 @@ function useSetupAnimatedValues<T>() {
}, []);
const autoScrollDistance = useDerivedValue(() => {
if (!isDraggingCell.value) return 0
if (!isDraggingCell.value) return 0;
const innerScrollDiff = scrollOffset.value - scrollInit.value;
// If list is nested there may be an outer scroll diff
const outerScrollDiff = outerScrollOffset.value - outerScrollInit.value
const outerScrollDiff = outerScrollOffset.value - outerScrollInit.value;
const scrollDiff = innerScrollDiff + outerScrollDiff;
return scrollDiff;
}, []);
@ -99,7 +106,6 @@ function useSetupAnimatedValues<T>() {
}, []);
const touchPositionDiffConstrained = useDerivedValue(() => {
const containerMinusActiveCell =
containerSize.value - activeCellSize.value + scrollOffset.value;
@ -122,7 +128,6 @@ function useSetupAnimatedValues<T>() {
return props.dragItemOverflow
? touchPositionDiff.value
: touchPositionDiffConstrained.value;
}, []);
const hoverOffset = useDerivedValue(() => {
@ -164,6 +169,8 @@ function useSetupAnimatedValues<T>() {
touchPositionDiff,
touchTranslate,
autoScrollDistance,
viewableIndexMin,
viewableIndexMax,
}),
[
activeCellOffset,
@ -185,9 +192,11 @@ function useSetupAnimatedValues<T>() {
touchPositionDiff,
touchTranslate,
autoScrollDistance,
viewableIndexMin,
viewableIndexMax,
]
);
useEffect(() => {
props.onAnimValInit?.(value);
}, [value]);

View File

@ -17,6 +17,8 @@ export function useCellTranslate({ cellIndex, cellSize, cellOffset }: Params) {
spacerIndexAnim,
placeholderOffset,
hoverAnim,
viewableIndexMin,
viewableIndexMax,
} = useAnimatedValues();
const { activeKey } = useDraggableFlatListContext();
@ -24,7 +26,14 @@ export function useCellTranslate({ cellIndex, cellSize, cellOffset }: Params) {
const { animationConfigRef } = useRefs();
const translate = useDerivedValue(() => {
if (!activeKey || activeIndexAnim.value < 0) return 0;
const isActiveCell = cellIndex === activeIndexAnim.value;
const isOutsideViewableRange =
!isActiveCell &&
(cellIndex < viewableIndexMin.value ||
cellIndex > viewableIndexMax.value);
if (!activeKey || activeIndexAnim.value < 0 || isOutsideViewableRange) {
return 0;
}
// Determining spacer index is hard to visualize. See diagram: https://i.imgur.com/jRPf5t3.jpg
const isBeforeActive = cellIndex < activeIndexAnim.value;
@ -80,7 +89,7 @@ export function useCellTranslate({ cellIndex, cellSize, cellOffset }: Params) {
if (activeIndexAnim.value < 0) return 0;
// Active cell follows touch
if (cellIndex === activeIndexAnim.value) {
if (isActiveCell) {
return hoverAnim.value;
}
@ -96,7 +105,7 @@ export function useCellTranslate({ cellIndex, cellSize, cellOffset }: Params) {
: 0;
return withSpring(translationAmt, animationConfigRef.current);
}, [activeKey]);
}, [activeKey, cellIndex]);
return translate;
}

View File

@ -5,14 +5,14 @@ import { useRef, useCallback } from "react";
// Useful for functions that depend on external state, but
// should not trigger effects when that external state changes.
export function useStableCallback<T extends (a?: any, b?: any, c?: any) => any>(
fn: T
) {
const fnRef = useRef(fn);
fnRef.current = fn;
const identityRetainingFn = useCallback(
(...args: Parameters<T>) => fnRef.current(...args),
export function useStableCallback<
T extends (arg1?: any, arg2?: any, arg3?: any) => any
>(cb: T) {
const cbRef = useRef(cb);
cbRef.current = cb;
const identityRetainingCb = useCallback(
(...args: Parameters<T>) => cbRef.current(...args),
[]
);
return identityRetainingFn as T;
return identityRetainingCb as T;
}