Skip to content

Commit

Permalink
Merge pull request #43 from Glazzes/dev
Browse files Browse the repository at this point in the history
fix(Gallery): fix onVerticalPull and onSwipe overlap
  • Loading branch information
Glazzes authored Aug 9, 2024
2 parents 8733691 + e5a358d commit 7395ef8
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 182 deletions.
68 changes: 28 additions & 40 deletions src/commons/hooks/usePanCommons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
SwipeDirection,
} from '../types';
import { useVector } from './useVector';
import getSwipeDirection from '../utils/getSwipeDirection';
import { getSwipeDirection } from '../utils/getSwipeDirection';

type PanCommmonOptions = {
container: SizeVector<SharedValue<number>>;
Expand All @@ -33,15 +33,13 @@ type PanCommmonOptions = {
offset: Vector<SharedValue<number>>;
panMode: PanMode;
scale: SharedValue<number>;
minScale: number;
maxScale: SharedValue<number>;
decay?: boolean;
boundFn: BoundsFuction;
userCallbacks: Partial<{
onGestureEnd: () => void;
onSwipe: (direction: SwipeDirection) => void;
onPanStart: PanGestureEventCallback;
onPanEnd: PanGestureEventCallback;
onSwipe: (direction: SwipeDirection) => void;
onOverPanning: (x: number, y: number) => void;
}>;
};
Expand All @@ -60,8 +58,6 @@ export const usePanCommons = (options: PanCommmonOptions) => {
offset,
panMode,
scale,
minScale,
maxScale,
decay,
boundFn,
userCallbacks,
Expand Down Expand Up @@ -96,9 +92,7 @@ export const usePanCommons = (options: PanCommmonOptions) => {
const toX = e.translationX + offset.x.value;
const toY = e.translationY + offset.y.value;

const toScale = clamp(scale.value, minScale, maxScale.value);

const { x: boundX, y: boundY } = boundFn(toScale);
const { x: boundX, y: boundY } = boundFn(scale.value);
const exceedX = Math.max(0, Math.abs(toX) - boundX);
const exceedY = Math.max(0, Math.abs(toY) - boundY);
isWithinBoundX.value = exceedX === 0;
Expand All @@ -110,34 +104,33 @@ export const usePanCommons = (options: PanCommmonOptions) => {
userCallbacks.onOverPanning(ex, ey);
}

// Simplify both pan modes in one condition due to their similarity
// Simplify both free and clamp pan modes in one condition due to their similarity
if (panMode !== PanMode.FRICTION) {
const isFree = panMode === PanMode.FREE;
translate.x.value = isFree ? toX : clamp(toX, -1 * boundX, boundX);
translate.y.value = isFree ? toY : clamp(toY, -1 * boundY, boundY);
detectorTranslate.x.value = translate.x.value;
detectorTranslate.y.value = translate.y.value;
return;
}

if (panMode === PanMode.FRICTION) {
const overScrollFraction =
Math.max(container.width.value, container.height.value) * 1.5;
const overScrollFraction =
Math.max(container.width.value, container.height.value) * 1.5;

if (isWithinBoundX.value) {
translate.x.value = toX;
} else {
const fraction = Math.abs(Math.abs(toX) - boundX) / overScrollFraction;
const frictionX = friction(clamp(fraction, 0, 1));
translate.x.value += e.changeX * frictionX;
}
if (isWithinBoundX.value) {
translate.x.value = toX;
} else {
const fraction = Math.abs(Math.abs(toX) - boundX) / overScrollFraction;
const frictionX = friction(clamp(fraction, 0, 1));
translate.x.value += e.changeX * frictionX;
}

if (isWithinBoundY.value) {
translate.y.value = toY;
} else {
const fraction = Math.abs(Math.abs(toY) - boundY) / overScrollFraction;
const frictionY = friction(clamp(fraction, 0, 1));
translate.y.value += e.changeY * frictionY;
}
if (isWithinBoundY.value) {
translate.y.value = toY;
} else {
const fraction = Math.abs(Math.abs(toY) - boundY) / overScrollFraction;
const frictionY = friction(clamp(fraction, 0, 1));
translate.y.value += e.changeY * frictionY;
}
};

Expand All @@ -161,16 +154,15 @@ export const usePanCommons = (options: PanCommmonOptions) => {

userCallbacks.onPanEnd && runOnJS(userCallbacks.onPanEnd)(e);

const toScale = clamp(scale.value, minScale, maxScale.value);
const { x: boundX, y: boundY } = boundFn(toScale);
const { x: boundX, y: boundY } = boundFn(scale.value);
const clampX: [number, number] = [-1 * boundX, boundX];
const clampY: [number, number] = [-1 * boundY, boundY];

const toX = clamp(translate.x.value, -1 * boundX, boundX);
const toY = clamp(translate.y.value, -1 * boundY, boundY);

const shouldDecayX = decay && isWithinBoundX.value;
const shouldDecayY = decay && isWithinBoundY.value;
const decayX = decay && isWithinBoundX.value;
const decayY = decay && isWithinBoundY.value;
const decayConfigX = {
velocity: e.velocityX,
clamp: clampX,
Expand All @@ -184,28 +176,24 @@ export const usePanCommons = (options: PanCommmonOptions) => {
};

detectorTranslate.x.value = translate.x.value;
detectorTranslate.x.value = shouldDecayX
detectorTranslate.x.value = decayX
? withDecay(decayConfigX)
: withTiming(toX);

translate.x.value = shouldDecayX
? withDecay(decayConfigX)
: withTiming(toX);
translate.x.value = decayX ? withDecay(decayConfigX) : withTiming(toX);

detectorTranslate.y.value = translate.y.value;
detectorTranslate.y.value = shouldDecayY
detectorTranslate.y.value = decayY
? withDecay(decayConfigY)
: withTiming(toY);

translate.y.value = shouldDecayY
? withDecay(decayConfigY)
: withTiming(toY);
translate.y.value = decayY ? withDecay(decayConfigY) : withTiming(toY);

const restX = Math.max(0, Math.abs(translate.x.value) - boundX);
const restY = Math.max(0, Math.abs(translate.y.value) - boundY);
gestureEnd.value = restX > restY ? translate.x.value : translate.y.value;

if (shouldDecayX && shouldDecayY) {
if (decayX && decayY) {
const config = restX > restY ? decayConfigX : decayConfigY;
gestureEnd.value = withDecay(config, (finished) => {
if (finished && userCallbacks.onGestureEnd) {
Expand Down
4 changes: 2 additions & 2 deletions src/commons/hooks/usePinchCommons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ export const usePinchCommons = (options: PinchOptions) => {

translate.x.value = withTiming(toX);
translate.y.value = withTiming(toY);
scale.value = withTiming(toScale, undefined, () => {
runOnJS(switchGesturesState)(true);
scale.value = withTiming(toScale, undefined, (finished) => {
finished && runOnJS(switchGesturesState)(true);
});

gestureEnd.value = withTiming(toValue, undefined, (finished) => {
Expand Down
22 changes: 11 additions & 11 deletions src/commons/utils/getSwipeDirection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ const SWIPE_TIME = 175;
const SWIPE_VELOCITY = 500;
const SWIPE_DISTANCE = 20;

export default function getSwipeDirection(
export const getSwipeDirection = (
e: PanGestureEvent,
options: SwipeDirectionOptions
): SwipeDirection | undefined {
): SwipeDirection | undefined => {
'worklet';

const { time, boundaries, position, translate } = options;
Expand All @@ -24,37 +24,37 @@ export default function getSwipeDirection(
const deltaY = Math.abs(position.y - e.absoluteY);
const { x: boundX, y: boundY } = boundaries;

const swipeR =
const swipeRight =
e.velocityX >= SWIPE_VELOCITY &&
deltaX >= SWIPE_DISTANCE &&
deltaTime <= SWIPE_TIME;

const inRightBound = translate.x === boundX;
if (swipeR && inRightBound) return SwipeDirection.RIGHT;
if (swipeRight && inRightBound) return SwipeDirection.RIGHT;

const swipeL =
const swipeLeft =
e.velocityX <= -1 * SWIPE_VELOCITY &&
deltaX >= SWIPE_DISTANCE &&
deltaTime <= SWIPE_TIME;

const inLeftBound = translate.x === -1 * boundX;
if (swipeL && inLeftBound) return SwipeDirection.LEFT;
if (swipeLeft && inLeftBound) return SwipeDirection.LEFT;

const swipeU =
const swipeUp =
e.velocityY <= -1 * SWIPE_VELOCITY &&
deltaY >= SWIPE_DISTANCE &&
deltaTime <= SWIPE_TIME;

const inUpperBound = translate.y === -1 * boundY;
if (swipeU && inUpperBound) return SwipeDirection.UP;
if (swipeUp && inUpperBound) return SwipeDirection.UP;

const swipeD =
const swipeDown =
e.velocityY >= SWIPE_VELOCITY &&
deltaY >= SWIPE_DISTANCE &&
deltaTime <= SWIPE_TIME;

const inLowerBound = translate.y === boundY;
if (swipeD && inLowerBound) return SwipeDirection.DOWN;
if (swipeDown && inLowerBound) return SwipeDirection.DOWN;

return undefined;
}
};
2 changes: 0 additions & 2 deletions src/components/crop/CropZoom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,6 @@ const CropZoom: React.FC<CropZoomProps> = (props) => {
translate,
offset,
scale,
minScale,
maxScale,
detectorTranslate,
panMode,
boundFn: boundsFn,
Expand Down
17 changes: 8 additions & 9 deletions src/components/gallery/Gallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ const Gallery = <T extends unknown>(props: GalleryPropsWithRef<T>) => {
const measureRoot = (e: LayoutChangeEvent) => {
rootSize.width.value = e.nativeEvent.layout.width;
rootSize.height.value = e.nativeEvent.layout.height;
scroll.value = activeIndex.value * e.nativeEvent.layout.width;

const direction = vertical ? rootSize.width.value : rootSize.height.value;
scroll.value = activeIndex.value * direction;
};

useAnimatedReaction(
Expand Down Expand Up @@ -133,7 +135,7 @@ const Gallery = <T extends unknown>(props: GalleryPropsWithRef<T>) => {

// Reference handling
const setIndex = (index: number) => {
const clamped = clamp(index, 0, data.length);
const clamped = clamp(index, 0, data.length - 1);
activeIndex.value = clamped;
fetchIndex.value = clamped;
scroll.value = clamped * itemSize.value;
Expand Down Expand Up @@ -162,19 +164,16 @@ const Gallery = <T extends unknown>(props: GalleryPropsWithRef<T>) => {
return (
<GestureHandlerRootView style={styles.root} onLayout={measureRoot}>
{data.map((item, index) => {
if (
index < scrollIndex - nextItems ||
index > scrollIndex + nextItems
) {
return null;
}
const inLowerHalf = index < scrollIndex - nextItems;
const inUpperHalf = index > scrollIndex + nextItems;
if (inLowerHalf || inUpperHalf) return null;

const key = keyExtractor?.(item, index) ?? `item-${index}`;

return (
<GalleryItem
key={key}
count={data.length}
zIndex={data.length - index}
index={index}
item={item}
vertical={vertical}
Expand Down
48 changes: 26 additions & 22 deletions src/components/gallery/GalleryItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { StyleSheet, type LayoutChangeEvent } from 'react-native';
import Animated, {
useAnimatedReaction,
useAnimatedStyle,
useDerivedValue,
useSharedValue,
} from 'react-native-reanimated';

Expand All @@ -15,37 +14,37 @@ import type { GalleryTransitionCallback } from './types';
type GalleryItemProps = {
item: any;
index: number;
count: number;
zIndex: number;
vertical: boolean;
renderItem: (item: any, index: number) => React.ReactElement;
customTransition?: GalleryTransitionCallback;
};

const GalleryItem: React.FC<GalleryItemProps> = ({
count,
index,
zIndex,
item,
vertical,
renderItem,
customTransition,
}) => {
const {
rootSize,
activeIndex,
rootChildSize,
activeIndex,
scroll,
isScrolling,
translate,
scale,
} = useContext(GalleryContext);

const childSize = useSizeVector(0, 0);
const innerSize = useSizeVector(0, 0);
const innerTranslate = useVector(0, 0);
const innerScale = useSharedValue<number>(1);

const measureChild = (e: LayoutChangeEvent) => {
childSize.width.value = e.nativeEvent.layout.width;
childSize.height.value = e.nativeEvent.layout.height;
innerSize.width.value = e.nativeEvent.layout.width;
innerSize.height.value = e.nativeEvent.layout.height;

if (index === activeIndex.value) {
rootChildSize.width.value = e.nativeEvent.layout.width;
Expand All @@ -61,7 +60,7 @@ const GalleryItem: React.FC<GalleryItemProps> = ({
{ scale: innerScale.value },
],
};
});
}, [innerTranslate, innerScale]);

const transitionStyle = useAnimatedStyle(() => {
if (customTransition !== undefined) {
Expand All @@ -88,33 +87,38 @@ const GalleryItem: React.FC<GalleryItemProps> = ({
return { transform: [{ translateX }], opacity };
});

useDerivedValue(() => {
if (index === activeIndex.value) {
innerTranslate.x.value = translate.x.value;
innerTranslate.y.value = translate.y.value;
innerScale.value = scale.value;
}
}, [activeIndex, translate, scale]);
useAnimatedReaction(
() => ({
activeIndex: activeIndex.value,
translate: { x: translate.x.value, y: translate.y.value },
scale: scale.value,
}),
(current) => {
if (index !== current.activeIndex) return;
innerTranslate.x.value = current.translate.x;
innerTranslate.y.value = current.translate.y;
innerScale.value = current.scale;
},
[activeIndex, translate, scale]
);

useAnimatedReaction(
() => activeIndex.value,
(value) => {
if (index === value) {
rootChildSize.width.value = childSize.width.value;
rootChildSize.height.value = childSize.height.value;
rootChildSize.width.value = innerSize.width.value;
rootChildSize.height.value = innerSize.height.value;
} else {
innerTranslate.x.value = 0;
innerTranslate.y.value = 0;
innerScale.value = 1;
}
},
[activeIndex]
[activeIndex, innerSize]
);

return (
<Animated.View
style={[styles.root, { zIndex: count - index }, transitionStyle]}
>
<Animated.View style={[styles.root, transitionStyle, { zIndex }]}>
<Animated.View style={childStyle} onLayout={measureChild}>
{renderItem(item, index)}
</Animated.View>
Expand All @@ -135,8 +139,8 @@ const styles = StyleSheet.create({

export default React.memo(GalleryItem, (prev, next) => {
return (
prev.count === next.count &&
prev.index === next.index &&
prev.zIndex === next.zIndex &&
prev.vertical === next.vertical &&
prev.customTransition === next.customTransition &&
prev.renderItem === next.renderItem
Expand Down
Loading

0 comments on commit 7395ef8

Please sign in to comment.