Skip to content

Commit c0e1c18

Browse files
committed
refactor(radio-button): migrate dot animation to reanimated
1 parent ee00e84 commit c0e1c18

3 files changed

Lines changed: 72 additions & 42 deletions

File tree

src/components/RadioButton/RadioButton.tsx

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
import * as React from 'react';
2-
import { Animated, StyleSheet, View } from 'react-native';
2+
import { StyleSheet, View } from 'react-native';
3+
4+
import Animated, {
5+
Easing,
6+
ReduceMotion,
7+
useAnimatedStyle,
8+
useSharedValue,
9+
withTiming,
10+
} from 'react-native-reanimated';
311

412
import { RadioButtonContext } from './RadioButtonGroup';
513
import { RadioButtonTokens } from './tokens';
614
import { getSelectionControlColor, handlePress, isChecked } from './utils';
715
import { useInternalTheme } from '../../core/theming';
16+
import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext';
817
import type { $RemoveChildren, ThemeProp } from '../../types';
918
import TouchableRipple from '../TouchableRipple/TouchableRipple';
1019

@@ -104,17 +113,30 @@ const RadioButton = ({
104113
value,
105114
}) === 'checked';
106115

116+
const reduceMotion = useReduceMotion();
117+
const reanimatedReduceMotion = reduceMotion
118+
? ReduceMotion.Always
119+
: ReduceMotion.Never;
120+
107121
// Single selection animation path: the dot scales in (with a slight
108122
// overshoot) when the radio becomes checked. The ring outline stays a
109123
// constant width. Keyed on `checked` (not `status`) so radios driven by a
110124
// `RadioButton.Group` animate too.
111-
const { current: radioAnim } = React.useRef<Animated.Value>(
112-
new Animated.Value(1)
113-
);
114-
125+
const dotScale = useSharedValue(1);
115126
const isFirstRendering = React.useRef<boolean>(true);
116127

117-
const { scale } = theme.animation;
128+
const dotTimingConfig = React.useMemo(
129+
() => ({
130+
duration: theme.motion.duration.short3,
131+
easing: Easing.bezier(...theme.motion.easing.standard),
132+
reduceMotion: reanimatedReduceMotion,
133+
}),
134+
[
135+
theme.motion.duration.short3,
136+
theme.motion.easing.standard,
137+
reanimatedReduceMotion,
138+
]
139+
);
118140

119141
React.useEffect(() => {
120142
// Do not run animation on very first rendering
@@ -124,15 +146,15 @@ const RadioButton = ({
124146
}
125147

126148
if (checked) {
127-
radioAnim.setValue(1.2);
128-
129-
Animated.timing(radioAnim, {
130-
toValue: 1,
131-
duration: 150 * scale,
132-
useNativeDriver: true,
133-
}).start();
149+
// Jump to the overshoot value, then settle back to 1.
150+
dotScale.value = 1.2;
151+
dotScale.value = withTiming(1, dotTimingConfig);
134152
}
135-
}, [checked, radioAnim, scale]);
153+
}, [checked, dotScale, dotTimingConfig]);
154+
155+
const dotAnimatedStyle = useAnimatedStyle(() => ({
156+
transform: [{ scale: dotScale.value }],
157+
}));
136158

137159
const { selectionControlColor, selectionControlOpacity } =
138160
getSelectionControlColor({
@@ -188,10 +210,8 @@ const RadioButton = ({
188210
<Animated.View
189211
style={[
190212
styles.dot,
191-
{
192-
backgroundColor: selectionControlColor,
193-
transform: [{ scale: radioAnim }],
194-
},
213+
{ backgroundColor: selectionControlColor },
214+
dotAnimatedStyle,
195215
]}
196216
/>
197217
</View>

src/components/__tests__/RadioButton/__snapshots__/RadioButton.test.tsx.snap

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -208,19 +208,24 @@ exports[`RadioButton when RadioButton is wrapped by RadioButtonContext.Provider
208208
}
209209
>
210210
<View
211-
collapsable={false}
212211
style={
213-
{
214-
"backgroundColor": "rgba(103, 80, 164, 1)",
215-
"borderRadius": 5,
216-
"height": 10,
217-
"transform": [
218-
{
219-
"scale": 1,
220-
},
221-
],
222-
"width": 10,
223-
}
212+
[
213+
{
214+
"borderRadius": 5,
215+
"height": 10,
216+
"width": 10,
217+
},
218+
{
219+
"backgroundColor": "rgba(103, 80, 164, 1)",
220+
},
221+
{
222+
"transform": [
223+
{
224+
"scale": 1,
225+
},
226+
],
227+
},
228+
]
224229
}
225230
/>
226231
</View>

src/components/__tests__/RadioButton/__snapshots__/RadioButtonGroup.test.tsx.snap

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,19 +82,24 @@ exports[`RadioButtonGroup renders properly 1`] = `
8282
}
8383
>
8484
<View
85-
collapsable={false}
8685
style={
87-
{
88-
"backgroundColor": "rgba(103, 80, 164, 1)",
89-
"borderRadius": 5,
90-
"height": 10,
91-
"transform": [
92-
{
93-
"scale": 1,
94-
},
95-
],
96-
"width": 10,
97-
}
86+
[
87+
{
88+
"borderRadius": 5,
89+
"height": 10,
90+
"width": 10,
91+
},
92+
{
93+
"backgroundColor": "rgba(103, 80, 164, 1)",
94+
},
95+
{
96+
"transform": [
97+
{
98+
"scale": 1,
99+
},
100+
],
101+
},
102+
]
98103
}
99104
/>
100105
</View>

0 commit comments

Comments
 (0)