Agletというスニーカーを集めるアプリがありますが、そのアプリで新たにアイテムが手に入った時に流れるアニメーションのインパクトが強かったため、react-native-skiaで再現してみました。ロゴとテキストが斜め横に無限に移動し続けます。
テキストとロゴは適当なものに変えています。
実装順序としては
という流れです。
テキストを画面に並べて横まで埋まるようにします。事前にテキスト(+ロゴ)の横幅を計算しておきます。以下のように、横に並べる数を画面サイズに応じて計算します。斜めになっても途切れないように、少し画面幅よりも多めに計算します。
Math.floor((windowSize.width * 1.3) / Constants.messageGroupWidth) + 1
そして横移動が無限ループに見えるように、グループを横にもう一つ追加します。なぜもうひとつ横並びにするかについては、以下の記事を読むと仕組みがわかりやすいです。
写真が画面の端から端へ流れる無限ループするアニメーション | chocolat | Freelance Frontend Developer based in Tokyo, Japan.
左方向に移動する場合には右に追加のグループを配置し、右方向に移動する場合は左に追加のグループを配置するようにします。このように配置しておき、横に移動させた後また元の位置に戻り移動を繰り返すと無限ループしているように見えます。
こうして作成した行を一定の間隔で縦に並べ画面を埋めます。
react-native-skiaの場合、以下でアニメーションをループさせることができます。
const progress = useSharedValue(0)
useEffect(() => {
progress.value = withRepeat(
withTiming(1, { duration: 5000 }),
-1,
false
)
}, [])
progressの値は0.0から1.0まで変化するのを繰り返します。
オプションのrepeat
の第二引数をfalseにします。trueにすると一度進みきったところから戻るアニメーションが発生してしまうためです。
このprogress
の値を元にしてグループに対して横移動をかけます。値が0のときは移動せず、値が1のときは画面幅の分、横に移動します。左に移動する場合はマイナスします。
const transform = useDerivedValue(() => {
const distance = progress.value * groupWidth
return [
{
translateX: distance * (props.direction === "left" ? -1 : 1),
},
]
}, [])
return (
<Group transform={transform}>
...
</Group>
)
角度を斜めにするには、全体をグループで覆ってtransformをかければOKです。
<Group
transform={[{ rotate: Math.PI / 12 }]}
origin={{ x: 0, y: 0 }}
>
...
</Group>
これで完成です。
実際のコードは以下で確認できます。
https://github.com/gaishimo/omoidasu-tech-blog/blob/main/components/pages/2022-08-13-aglet-animation/AgletLikeAnimation/index.tsx