Skip to content

Commit f76ed12

Browse files
committed
feat: migrate PressableArea and TabBarButton to react-native-ease
- Add react-native-ease dependency for lightweight native animations - Migrate PressableArea: replace Reanimated opacity with EaseView - Migrate TabBarButton: replace Reanimated scale with EaseView spring - Add Jest mock for react-native-ease in test setup - Update investigation doc with implementation status
1 parent 902299f commit f76ed12

File tree

4 files changed

+51
-43
lines changed

4 files changed

+51
-43
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@
154154
"react-dom": "19.1.0",
155155
"react-native": "0.81.5",
156156
"react-native-compressor": "^1.13.0",
157+
"react-native-ease": "^0.2.0",
157158
"react-native-edge-to-edge": "1.6.0",
158159
"react-native-fs": "^2.20.0",
159160
"react-native-gesture-handler": "~2.28.0",

src/components/PressableArea/PressableArea.tsx

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,36 @@
1-
import { PropsWithChildren, Ref, forwardRef } from 'react';
1+
import { PropsWithChildren, Ref, forwardRef, useState } from 'react';
22
// eslint-disable-next-line no-restricted-imports
33
import { Pressable, PressableProps, View } from 'react-native';
4-
import Animated, {
5-
useAnimatedStyle,
6-
useSharedValue,
7-
withTiming,
8-
} from 'react-native-reanimated';
9-
10-
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
4+
import { EaseView } from 'react-native-ease';
115

126
export const PressableArea = forwardRef(
137
(props: PropsWithChildren<PressableProps>, ref: Ref<View>) => {
14-
const opacity = useSharedValue(1);
8+
const [pressed, setPressed] = useState(false);
159

16-
const animatedStyle = useAnimatedStyle(() => ({
17-
opacity: opacity.value,
18-
}));
10+
const { onPressIn, onPressOut, style, children, ...rest } = props;
1911

2012
return (
21-
<AnimatedPressable
22-
{...props}
13+
<Pressable
14+
{...rest}
2315
ref={ref}
24-
style={[props.style, animatedStyle]}
25-
onPressIn={() => {
26-
opacity.value = withTiming(0.75, { duration: 150 });
16+
style={style}
17+
onPressIn={e => {
18+
setPressed(true);
19+
onPressIn?.(e);
2720
}}
28-
onPressOut={() => {
29-
opacity.value = withTiming(1, { duration: 150 });
21+
onPressOut={e => {
22+
setPressed(false);
23+
onPressOut?.(e);
3024
}}
31-
/>
25+
>
26+
<EaseView
27+
animate={{ opacity: pressed ? 0.75 : 1 }}
28+
transition={{ type: 'timing', duration: 150 }}
29+
style={[{ flex: 1 }]}
30+
>
31+
{children}
32+
</EaseView>
33+
</Pressable>
3234
);
3335
},
3436
);

src/components/TabBarButton/TabBarButton.tsx

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as Haptics from 'expo-haptics';
2-
import { FC, ReactNode } from 'react';
2+
import { FC, ReactNode, useState } from 'react';
33
import {
44
AccessibilityState,
55
GestureResponderEvent,
@@ -8,13 +8,7 @@ import {
88
Pressable,
99
StyleSheet,
1010
} from 'react-native';
11-
import Animated, {
12-
useAnimatedStyle,
13-
useSharedValue,
14-
withSpring,
15-
} from 'react-native-reanimated';
16-
17-
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
11+
import { EaseView } from 'react-native-ease';
1812

1913
interface TabBarButtonProps {
2014
icon: FC<{ color: string }>;
@@ -33,11 +27,7 @@ export function TabBarButton({
3327
}: TabBarButtonProps) {
3428
const focused = accessibilityState?.selected;
3529
const color = focused ? activeTintColor : inactiveTintColor;
36-
const scale = useSharedValue(1);
37-
38-
const animatedStyle = useAnimatedStyle(() => ({
39-
transform: [{ scale: scale.value }],
40-
}));
30+
const [pressed, setPressed] = useState(false);
4131

4232
const handlePress = (e: GestureResponderEvent) => {
4333
if (Platform.OS !== 'web') {
@@ -49,18 +39,24 @@ export function TabBarButton({
4939
};
5040

5141
return (
52-
<AnimatedPressable
42+
<Pressable
5343
onPress={handlePress}
54-
onPressIn={() => {
55-
scale.value = withSpring(0.92);
56-
}}
57-
onPressOut={() => {
58-
scale.value = withSpring(1);
59-
}}
60-
style={[styles.pressable, animatedStyle]}
44+
onPressIn={() => setPressed(true)}
45+
onPressOut={() => setPressed(false)}
46+
style={styles.pressable}
6147
>
62-
{icon({ color }) as ReactNode}
63-
</AnimatedPressable>
48+
<EaseView
49+
animate={{ scale: pressed ? 0.92 : 1 }}
50+
transition={{
51+
type: 'spring',
52+
damping: 25,
53+
stiffness: 300,
54+
}}
55+
style={styles.pressable}
56+
>
57+
{icon({ color }) as ReactNode}
58+
</EaseView>
59+
</Pressable>
6460
);
6561
}
6662

test/setupTests.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,12 @@ jest.mock('expo-updates');
131131
jest.mock('expo/fetch');
132132

133133
jest.mock('pressto');
134+
135+
jest.mock('react-native-ease', () => {
136+
const React = require('react');
137+
const { View } = require('react-native');
138+
return {
139+
EaseView: ({ children, style, ...rest }: { children?: React.ReactNode; style?: unknown }) =>
140+
React.createElement(View, { ...rest, style }, children),
141+
};
142+
});

0 commit comments

Comments
 (0)