参考
ジェスチャーの実装に入っていきます。
ライブラリのインストール
ReactNativeGestureHandlerをインストールします。
また、ジェスチャー中のアニメーションを実装するために、react-native-reanimatedもインストールします。
$ npx expo install react-native-gesture-handler react-native-reanimated
$ npm install -D @babel/plugin-proposal-export-namespace-from
ReanimatedのBabelプラグインをbabel.config.jsに追加します。
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
"@babel/plugin-proposal-export-namespace-from",
"react-native-reanimated/plugin",
],
};
};
開発サーバーを再起動します。
babel.config.jsの変更を反映させるために、-cをつけて起動しています。
$ npx expo start -c
アプリ内でジェスチャを動かすには、アプリの最上位にラップする必要があります。
App.jsに追加します。
import { GestureHandlerRootView } from "react-native-gesture-handler";
export default function App() {
return (
<GestureHandlerRootView style={styles.container}>
/* ...rest of the code remains */
</GestureHandlerRootView>
)
}
コンポーネントをアニメーション化する
EmojiStickerをアニメーション化します。
ImageComponentをAnimated.Imageに修正します。
import Animated from 'react-native-reanimated';
<Animated.Image
source={stickerSource}
resizeMode="contain"
style={{ width: imageSize, height: imageSize }}
/>
タップジェスチャを追加する
useSharedValueで、アニメーションに使う値を設定します。
今回は画像サイズをアニメーションに使います。
const scaleImage = useSharedValue(imageSize);
doubleTap時の処理を追加します。
下記のコードでは、2回タップされたら、画像のサイズを2倍にする処理になっています。
const doubleTap = Gesture.Tap()
.numberOfTaps(2)
.onStart(() => {
if (scaleImage.value !== imageSize * 2) {
scaleImage.value = scaleImage.value * 2;
}
});
useAnimatedStyle()で、アニメーションに使うStyleを設定します。
const imageStyle = useAnimatedStyle(() => {
return {
width: withSpring(scaleImage.value),
height: withSpring(scaleImage.value),
};
});
下記2点を行います。
- GestureDetectorのgesturePropsへ、doubleTap時の処理を渡す。
- Animated.ImageのStyleへ、useAnimatedStyle()で作成したimageStyleを追加する。
<GestureDetector gesture={doubleTap}>
<Animated.Image
source={stickerSource}
resizeMode="contain"
style={[imageStyle, { width: imageSize, height: imageSize }]}
/>
</GestureDetector>
すべてのコードは下記になります。
import { View } from "react-native";
import { Gesture, GestureDetector } from "react-native-gesture-handler";
import Animated, {
useAnimatedStyle,
useSharedValue,
withSpring,
} from "react-native-reanimated";
export default function EmojiSticker({ imageSize, stickerSource }) {
const scaleImage = useSharedValue(imageSize);
const doubleTap = Gesture.Tap()
.numberOfTaps(2)
.onStart(() => {
if (scaleImage.value !== imageSize * 2) {
scaleImage.value = scaleImage.value * 2;
}
});
const imageStyle = useAnimatedStyle(() => {
return {
width: withSpring(scaleImage.value),
height: withSpring(scaleImage.value),
};
});
return (
<View style={{ top: -350 }}>
<GestureDetector gesture={doubleTap}>
<Animated.Image
source={stickerSource}
resizeMode="contain"
style={{ width: imageSize, height: imageSize }}
/>
</GestureDetector>
</View>
);
}
この変更後、アイコンをダブルタップ(Webではダブルクリック)することで、画像がアニメーションしながら2倍サイズになります。
パンジェスチャを追加する
EmojiSticker.jsのViewComponentをAnimatedにします。
+ <Animated.View style={{ top: -350 }}>
<GestureDetector gesture={doubleTap}>
{/* ...rest of the code remains same */}
</GestureDetector>
+ </Animated.View>
X,Y座標を保存する値を、useSharedValue
で作成します。
export default function EmojiSticker({ imageSize, stickerSource }) {
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
// ...rest of the code remains same
}
ダブルタップの処理を追加したときと似たように、Panジェスチャーを実装します。
onChangeイベントのeventから、指の位置を取得し、先程作成した、translate変数へ入れる処理にします。
const drag = Gesture.Pan()
.onChange((event) => {
translateX.value += event.changeX;
translateY.value += event.changeY;
});
useAnimatedStyleで座標のスタイルを作成します。
const containerStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateX: translateX.value,
},
{
translateY: translateY.value,
},
],
};
});
下記2点を行います。DoubleTapと同様です。
- GestureDetectorを再度追加し、drag関数を渡す。
- Animated.ViewのstyleへuseAnimatedStyleで作成したcontainerStyleを渡す。
+ <GestureDetector gesture={drag}>
+ <Animated.View style={[containerStyle, { top: -350 }]}>
<GestureDetector gesture={doubleTap}>
<Animated.Image
source={stickerSource}
resizeMode="contain"
style={[imageStyle, { width: imageSize, height: imageSize }]}
/>
</GestureDetector>
+ </Animated.View>
+ </GestureDetector>
上記でアイコンを好き場所にドラッグすることができるようになりました。