よくUIのローディング中の表示で使われるのがSkeleton Screenです。
Facebookなどで使われているローディングの名称を知ってる? / 調べたらたくさんありすぎた話 - omuriceman's blog
React Nativeで実装する場合 react-content-loader や react-native-svg-animated-linear-gradient 等のライブラリを使う方法がありますが、これらは基本的にSVGを利用しています。今回はSkia(react-native-skia)を用いて同様のものを実装してみたいと思います。
前回の記事 でテキストにグラデーションを設定しグラデーションを変化させるアニメーションを実装しましたが、仕組み的には同じのため、同様の作りで実装してみたいと思います。
グラデーションには<LinearGradient />
を使います。
<LinearGradient
start={{ x: 0, y: 150 }}
end={{ x: 300, y: 150 }}
colors={[basicColor, accentColor, basicColor]}
/>
これをアニメーションで動的にグラデーションが移動するようにしていきます。実装の仕方は前回の記事 と同じですが、繰り返しアニメーションを指定したsharedValue
を用意し(progress
)、この値に応じてuseDerivedValue
で<LinearGradient />
のポイント位置を動的に変化させます。
const progress = useSharedValue(0)
useEffect(() => {
progress.value = withRepeat(
withTiming(1, {
easing: Easing.inOut(Easing.ease),
duration: 2300,
}),
-1,
false,
)
}, [])
const positions = useDerivedValue(() => {
return [
interpolate(progress.value, [0, 0.25, 0.5, 0.75, 1], [0, 0, 0, 0.5, 1]),
interpolate(progress.value, [0, 0.25, 0.5, 0.75, 1], [0, 0, 0.5, 1, 1]),
interpolate(progress.value, [0, 0.25, 0.5, 0.75, 1], [0, 0.5, 1, 1, 1]),
]
}, [])
type Props = {
basicColor: string
accentColor: string
canvasSize: { width: number; height: number }
children: ReactNode
}
export function SkeletonViewContainer(props: Props) {
const progress = useSharedValue(0)
useEffect(() => {
progress.value = withRepeat(
withTiming(1, {
easing: Easing.inOut(Easing.ease),
duration: 2300,
}),
-1,
false,
)
}, [])
const positions = useDerivedValue(() => {
return [
interpolate(progress.value, [0, 0.25, 0.5, 0.75, 1], [0, 0, 0, 0.5, 1]),
interpolate(progress.value, [0, 0.25, 0.5, 0.75, 1], [0, 0, 0.5, 1, 1]),
interpolate(progress.value, [0, 0.25, 0.5, 0.75, 1], [0, 0.5, 1, 1, 1]),
]
}, [])
return (
<Canvas style={props.canvasSize}>
<Group>
<LinearGradient
start={{ x: 0, y: props.canvasSize.height % 2 }}
end={{ x: props.canvasSize.width, y: props.canvasSize.height % 2 }}
colors={[props.basicColor, props.accentColor, props.basicColor]}
positions={positions}
/>
{props.children}
</Group>
</Canvas>
)
}
外側からは以下のようにコンテンツの中身をchildrenに指定してrenderさせます。
<SkeletonViewContainer
basicColor="rgb(245, 245, 245)"
accentColor="rgb(225, 225, 225)"
canvasSize={canvasSize}
>
<RoundedRect x={0} y={0} r={8} width={70} height={70} />
<RoundedRect x={80} y={17} r={8} width={250} height={13} />
<RoundedRect x={80} y={40} r={8} width={250} height={10} />
<RoundedRect x={0} y={80} r={8} width={330} height={10} />
<RoundedRect x={0} y={100} r={8} width={200} height={10} />
<RoundedRect x={0} y={120} r={8} width={330} height={10} />
</SkeletonViewContainer>
以下のようにSkeleton Screenが表示されます!
こちら で確認できます。