跳转到主要内容

手势

当与 reanimated 集成时,我们建议使用 react-native-gesture-handler

我们准备了一些教程,展示了在 Skia 绘图中使用高级手势。

tsx
import { useWindowDimensions } from "react-native";
import { Canvas, Circle, Fill } from "@shopify/react-native-skia";
import { GestureDetector, Gesture } from "react-native-gesture-handler";
import { useSharedValue, withDecay } from "react-native-reanimated";
 
export const AnimationWithTouchHandler = () => {
const { width } = useWindowDimensions();
const leftBoundary = 0;
const rightBoundary = width;
const translateX = useSharedValue(width / 2);
 
const gesture = Gesture.Pan()
.onChange((e) => {
translateX.value += e.changeX;
})
.onEnd((e) => {
translateX.value = withDecay({
velocity: e.velocityX,
clamp: [leftBoundary, rightBoundary],
});
});
 
return (
<GestureDetector gesture={gesture}>
<Canvas style={{ flex: 1 }}>
<Fill color="white" />
<Circle cx={translateX} cy={40} r={20} color="#3E3E" />
</Canvas>
</GestureDetector>
);
};
tsx
import { useWindowDimensions } from "react-native";
import { Canvas, Circle, Fill } from "@shopify/react-native-skia";
import { GestureDetector, Gesture } from "react-native-gesture-handler";
import { useSharedValue, withDecay } from "react-native-reanimated";
 
export const AnimationWithTouchHandler = () => {
const { width } = useWindowDimensions();
const leftBoundary = 0;
const rightBoundary = width;
const translateX = useSharedValue(width / 2);
 
const gesture = Gesture.Pan()
.onChange((e) => {
translateX.value += e.changeX;
})
.onEnd((e) => {
translateX.value = withDecay({
velocity: e.velocityX,
clamp: [leftBoundary, rightBoundary],
});
});
 
return (
<GestureDetector gesture={gesture}>
<Canvas style={{ flex: 1 }}>
<Fill color="white" />
<Circle cx={translateX} cy={40} r={20} color="#3E3E" />
</Canvas>
</GestureDetector>
);
};

元素跟踪

一个常见的用例是仅对画布上的特定元素激活手势。手势处理器在这方面表现出色,因为它可以考虑应用于元素的所有变换,例如平移、缩放和旋转。为了跟踪每个元素,在其上覆盖一个动画视图,确保应用于画布元素的相同变换也镜像到动画视图上。

在下面的示例中,每个圆都由两个手势处理器分别跟踪。

tsx
import { View } from "react-native";
import { Canvas, Circle, Fill } from "@shopify/react-native-skia";
import { GestureDetector, Gesture } from "react-native-gesture-handler";
import Animated, { useSharedValue, useAnimatedStyle } from "react-native-reanimated";
 
const radius = 30;
 
export const ElementTracking = () => {
// The position of the ball
const x = useSharedValue(100);
const y = useSharedValue(100);
// This style will be applied to the "invisible" animated view
// that overlays the ball
const style = useAnimatedStyle(() => ({
position: "absolute",
top: -radius,
left: -radius,
width: radius * 2,
height: radius * 2,
transform: [{ translateX: x.value }, { translateY: y.value }],
}));
// The gesture handler for the ball
const gesture = Gesture.Pan().onChange((e) => {
x.value += e.x;
y.value += e.y;
});
return (
<View style={{ flex: 1 }}>
<Canvas style={{ flex: 1 }}>
<Fill color="white" />
<Circle cx={x} cy={y} r={radius} color="cyan" />
</Canvas>
<GestureDetector gesture={gesture}>
<Animated.View style={style} />
</GestureDetector>
</View>
);
};
tsx
import { View } from "react-native";
import { Canvas, Circle, Fill } from "@shopify/react-native-skia";
import { GestureDetector, Gesture } from "react-native-gesture-handler";
import Animated, { useSharedValue, useAnimatedStyle } from "react-native-reanimated";
 
const radius = 30;
 
export const ElementTracking = () => {
// The position of the ball
const x = useSharedValue(100);
const y = useSharedValue(100);
// This style will be applied to the "invisible" animated view
// that overlays the ball
const style = useAnimatedStyle(() => ({
position: "absolute",
top: -radius,
left: -radius,
width: radius * 2,
height: radius * 2,
transform: [{ translateX: x.value }, { translateY: y.value }],
}));
// The gesture handler for the ball
const gesture = Gesture.Pan().onChange((e) => {
x.value += e.x;
y.value += e.y;
});
return (
<View style={{ flex: 1 }}>
<Canvas style={{ flex: 1 }}>
<Fill color="white" />
<Circle cx={x} cy={y} r={radius} color="cyan" />
</Canvas>
<GestureDetector gesture={gesture}>
<Animated.View style={style} />
</GestureDetector>
</View>
);
};