step2.MP4
step1.mov
[1] Create a `Gesture.Tap` and apply it to the `knob` view using `GestureDetector`
const tapGesture = Gesture.Tap()
<GestureDetector gesture={tapGesture}>
<Animated.View style={styles.knob} />
</GestureDetector>
[2] `tapGesture` should set a scale value based on which we are going to animate the knob. When gesture ends, we are bringing back the scale to the initial value
create a `scale` sharedValue starting from 1
const scale = useSharedValue(1);
add onBegin method and change `scale` value using spring
.onBegin(() => {
scale.value = withSpring(2)
})
add onEnd method to bring back the scale to using spring
.onEnd(() => {
scale.value = withSpring(1)
})
create a knobStyle using useAnimatedStyle and change the scale and borderWidth by interpolating scale.value `[1,2] → [layout.knobSize / 2, 2]`
const animatedStyle = useAnimatedStyle(() => {
return {
borderWidth: interpolate(
scale.value,
[1, 2],
[layout.knobSize / 2, 2],
Extrapolation.CLAMP
),
transform: [
{
scale: scale.value,
},
],
};
});
apply this style to the knob
<Animated.View style={[styles.knob, animatedStyle]} hitSlop={hitSlop} />
step2.MP4
Create a Pan gesture, combine it with Tap gesture. Using Pan gesture, we can get the x
coordinate of the pan, assign it to a sharedValue
that's starting from 0 and use this to apply the knob translateX
position.
[1] let’s use the same principle and create a `Gesture.Pan`
const panGesture = Gesture.Pan();
[2] `panGesture` should set a `x` value based on which we are going to move/animate the knob. When gesture ends, we are bringing back the `x` to the initial value (0)
create a `x` sharedValue starting from 1
const x = useSharedValue(0);
add onChange method and change x value based on `changeX`
.onChange((ev) => {
x.value += ev.changeX
})
changeX
instead of translationX
is that we would like to start from where we left when the gesture is triggered again (aka when we start panning again), in other words it keeps the knob in place and next time will move from the current position
when gesture has finished, bring back the knob `scale` to 1.
.onEnd(() => {
scale.value = withSpring(1)
})
apply `transform.translateX` as style using `x` shared value
const animatedStyle = useAnimatedStyle(() => {
return {
borderWidth: //
transform: [
{
translateX: x.value // <--------- here
},
{
scale: scale.value,
},
],
}
})
[3] Add both `Tap` and `Pan` as simulataneous gestures and apply it to the `GestureDetector`
const gestures = Gesture.Simultaneous(tapGesture, panGesture)
<GestureDetector gesture={gestures}>
//
</GestureDetector>
step1.mov
remove tapGesture
and use just the panGesture
as gesture on GestureDetector
. Create an isInteracting
shared value and replace the scale
with a derivedValue
that's going to animate using withSpring()
based on isInteracting
value.
[1] Create an `isInteracting` shared value and replace the `scale` with a `derivedValue` that's going to animate using `withSpring()` based on `isInteracting` value.
create isInteracting value and replace scale with a derived value.
const isInteracting = useSharedValue(false);
const scale = useDerivedValue(() => {
return withSpring(isInteracting.value ? 2 : 1);
});
using `.onBegin` and `.onFinalize` to toggle `isInteractive` value
.onBegin(() => {
isInteracting.value = true
})
.onFinalize(() => {
isInteracting.value = false
})
using `.onEnd` to bring back `x` to the initial value
.onEnd(() => {
x.value = withSpring(0)
})
Go to: Balloon Slider