perf: do not animate outside visible range
This commit is contained in:
parent
abf9105e1a
commit
355edf9ebe
@ -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}
|
||||
|
||||
@ -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]);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user