react-native-skiaでぼんやり光るライトを表現してみたいと思います。
ぼんやり光る状態をBlur
をつかってぼかして表現してみます。本体の円を配置しその裏にぼかしたサイズ違いの円を何枚か重ねます。外側が自然に薄くなっていくようにRadialGradient
を使って透明度を徐々に薄くしていっています。
const blurredCircles = [
{ radius: 40, blur: 40 },
{ radius: 50, blur: 60 },
{ radius: 60, blur: 80 },
{ radius: 70, blur: 100 },
]
<Canvas ...>
{blurredCircles.map((c, i) => (
<Circle key={i} cx={center.x} cy={center.y} r={c.radius}>
<RadialGradient
c={center}
r={c.radius}
colors={[color, colorAlpha]}
/>
<Blur blur={c.blur} mode="decal" />
</Circle>
))}
<Circle cx={center.x} cy={center.y} r={30} color={color}>
<Blur blur={0.7} mode="decal" />
</Circle>
</Canvas>
裏の円のサイズとぼかしの値、サイズはあくまで見た感覚を元に調整しています。
これにアニメーションを設定してみます。タップするとON/OFFが切り替わります。
タップした際にアニメーションValue(progress)を変化させ、その値に応じて裏側の円の半径を変更していきます。合わせて本体の円の不透明度も変化させていきます。
const progress = useSharedValue(0)
const gesture = Gesture.Tap()
.onStart(() => {
if (progress.value === 0) {
progress.value = withTiming(1, { duration: 350 })
} else {
progress.value = withTiming(0, { duration: 350 })
}
})
<GestureDetector gesture={gesture}>
<Canvas
...
>
<Group color={color}>
{circles.map((c, i) => (
<Circle
key={i}
cx={center.x}
cy={center.y}
r={useDerivedValue(() => {
return progress.value * c.radius
}, [])}
>
<RadialGradient
c={center}
r={c.radius}
colors={[color, colorAlpha]}
/>
<Blur blur={c.blur} mode="decal" />
</Circle>
))}
<Circle
cx={center.x}
cy={center.y}
r={30}
color={color}
opacity={useDerivedValue(() => {
return 0.4 + (progress.value * 0.6)
}, [])}
>
<Blur blur={0.7} mode="decal" />
</Circle>
</Group>
</Canvas>
</GestureDetector>
光の範囲や円の周囲の見た目など、更に調整すればもっと自然な形にできそうです。またSkiaを使った場合他にもっとよい表現方法があるかもしれません。
完成形のソースコードは こちらで確認できます。