Skip to content

Commit

Permalink
Merge pull request #576 from nmassey/fix-panOffset-race-condition
Browse files Browse the repository at this point in the history
fix: flicker when starting pan: `panOffset` value race condition
  • Loading branch information
dohooo authored Sep 11, 2024
2 parents 382cf29 + a99f069 commit 6794ac6
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/spotty-melons-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'react-native-reanimated-carousel': patch
---

fix: rework code to avoid possible flicker when starting pan (panOffset race condition)
23 changes: 22 additions & 1 deletion src/components/ScrollViewGesture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
const maxPage = dataLength;
const isHorizontal = useDerivedValue(() => !vertical, [vertical]);
const max = useSharedValue(0);
const panOffset = useSharedValue(0);
const panOffset = useSharedValue<number | undefined>(undefined); // set to undefined when not actively in a pan gesture
const touching = useSharedValue(false);
const validStart = useSharedValue(false);
const scrollEndTranslation = useSharedValue(0);
Expand Down Expand Up @@ -292,6 +292,20 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
const onGestureUpdate = useCallback((e: PanGestureHandlerEventPayload) => {
"worklet";

if (panOffset.value === undefined) {
// This may happen if `onGestureStart` is called as a part of the
// JS thread (instead of the UI thread / worklet). If so, when
// `onGestureStart` sets panOffset.value, the set will be asynchronous,
// and so it may not actually occur before `onGestureUpdate` is called.
//
// Keeping this value as `undefined` when it is not active protects us
// from the situation where we may use the previous value for panOffset
// instead; this would cause a visual flicker in the carousel.

// console.warn("onGestureUpdate: panOffset is undefined");
return;
}

if (validStart.value) {
validStart.value = false;
cancelAnimation(translation);
Expand Down Expand Up @@ -336,6 +350,11 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
const onGestureEnd = useCallback((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>, _success: boolean) => {
"worklet";

if (panOffset.value === undefined) {
// console.warn("onGestureEnd: panOffset is undefined");
return;
}

const { velocityX, velocityY, translationX, translationY } = e;
const scrollEndVelocityValue = isHorizontal.value
? velocityX
Expand Down Expand Up @@ -382,6 +401,8 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {

if (!loop)
touching.value = false;

panOffset.value = undefined;
}, [
size,
loop,
Expand Down

0 comments on commit 6794ac6

Please sign in to comment.