React Native Skiaを使って動くチェックボックス作ってみた
どうも、nano72mknです。
今回はReact Native Skiaを使って「動く!チェックボックス」を作ってみました!
とりあえず、この方法で出来たぞって感じなので間違えてるかもしれません!
Skiaってなに?
SkiaとはGoogleが開発しているオープンソースの2Dグラフィックライブラリで、
Google Chrome、ChromeOS、Android、Flutterなどで使われているっぽいですね。
ちなみに、Flutterのドキュメントを見ると今後Skiaの後継のImpellerになっていくっぽいですね
本題:チェックボックスを作っていく
完成品はこちら↓
このチェックボックスを作っていきます
四角形を作る
Rectangleを指定する
const x = 0;
const y = 0;
const width = 100;
const height = 100;
const rectangle = rect(x, y, width, height);
rectangle
を追加
SkPathを生成し、Skia.Path.Make
でSkPath
を生成し、SkPath
の引数にRectangle
を追加
const rectPath = Skia.Path.Make();
rectPath.addRect(rectangle);
SkPathを描画してみる
作ったSkPath
はPathコンポーネントを使って描画します。
<Path path={rectPath} color="#ffffff" />
画面上に表示する際は、Canvasコンポーネントで囲ってあげる必要があります。
style
にはwidth
とheight
を指定して、描画エリアを確保する必要があります。
<Canvas style={{ width, height }}>
<Path path={rectPath} color="#ffffff" />
</Canvas>
描画した四角形
コードの全体
import { Skia, rect, Path, Canvas } from '@shopify/react-native-skia';
const x = 0;
const y = 0;
const width = 100;
const height = 100;
const rectangle = rect(x, y, width, height);
const rectPath = Skia.Path.Make();
rectPath.addRect(rectangle);
export default function Index() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Canvas style={{ width, height }}>
<Path path={rectPath} color="#ffffff" />
</Canvas>
</View>
);
}
作った四角を角丸にする
先ほどのスクショを見てもらったらわかる通り、めっちゃ角が尖っているので丸くしてあげます。
rectangle
に角丸を指定する
作ったrrect
に先ほど作ったrectangle
を食わせて、角丸を指定します。
+const rx = 10;
+const ry = 10;
const rectangle = rect(x, y, width, height);
+const roundedRect = rrect(rectangle, rx, ry);
SkPathを微修正
addRect
からaddRRect
に変更してroundedRect
を渡します
const rectPath = Skia.Path.Make();
-rectPath.addRect(rectangle);
+rectPath.addRRect(roundedRect);
描画した角丸四角形
コードの全体
import { Skia, rect, Path, Canvas, rrect } from '@shopify/react-native-skia';
const x = 0;
const y = 0;
const width = 100;
const height = 100;
const rx = 10;
const ry = 10;
const rectangle = rect(x, y, width, height);
const roundedRect = rrect(rectangle, rx, ry);
const rectPath = Skia.Path.Make();
rectPath.addRRect(roundedRect);
export default function Index() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Canvas style={{ width, height }}>
<Path path={rectPath} color="#ffffff" />
</Canvas>
</View>
);
}
四角形の枠線を作る
style
をstroke
に指定
strokeWidth
で枠線の太さを指定できます。
const strokeWidth = 5;
...
<Path path={rectPath} color="#3ea8ff" style="stroke" strokeWidth={strokeWidth} />
このまま描画すると、スクショのように不恰好になります
これは、Canvas
のサイズとrectPath
の描画位置が線の太さを考慮されていない為、線の半分がはみ出してしまい不恰好になりました。
線の太さを考慮して調整する
四角形の位置を調整する
x
とy
は、はみ出している線の太さの半分ずらしてあげます
+const strokeWidth = 5;
-const x = 0;
+const x = 0 + strokeWidth / 2;
-const y = 0;
+const y = 0 + strokeWidth / 2;
Canvasのサイズを調整する
上下左右に線の太さの半分がはみ出ているので、足して線の太さ分大きくしてあげます。
+const canvasWidth = width + strokeWidth * 2;
+const canvasHeight = height + strokeWidth * 2;
...
- <Canvas style={{ width, height }}>
+ <Canvas style={{ width: canvasWidth, height: canvasHeight }}>
描画してみる
位置を調整した後は、きれいに表示されています!
コードの全体
import { Skia, rect, Path, Canvas, rrect } from '@shopify/react-native-skia';
+const strokeWidth = 5;
-const x = 0;
+const x = 0 + strokeWidth / 2;
-const y = 0;
+const y = 0 + strokeWidth / 2;
const width = 100;
const height = 100;
...
+const canvasWidth = width + strokeWidth * 2;
+const canvasHeight = height + strokeWidth * 2;
...
export default function Index() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
- <Canvas style={{ width, height }}>
+ <Canvas style={{ width: canvasWidth, height: canvasHeight }}>
<Path path={rectPath} color="#ffffff" />
<Path path={rectPath} color="#3ea8ff" style="stroke" strokeWidth={strokeWidth} />
</Canvas>
</View>
);
}
チェックマークを作る
一番楽しいところである、チェックマークを作っていきます。
SkPathを作る
今回は、SkPath
のlineTo
を使って描画していきます。
前回のrectangle
と同じように、Skia.Path.Make
します。
const checkPath = Skia.Path.Make();
lineを引く
まず最初に、漢字でいう「一画目」、書道でいう「起筆」「入り」の位置を決めます。
左から1/4、上から半分くらいの位置にポンと筆を立てます
checkPath.moveTo(25, 50);
あとは感性に従いながら、チェックマークの折り返し地点まで筆をすすめ
checkPath.lineTo(40, 70);
勢いよく「終筆」を決める。
checkPath.lineTo(85, 35);
完成したものをCanvas
で描画すると
<Path path={checkPath} color="#3ea8ff" style="stroke" strokeWidth={10} />
こうなります。
うまく説明できなくてすみません...w
moveTo
、lineTo
はもしかしたら計算式とかなんかゴニョごにょするものがあるかもしれないです。
が、感性に従って書くのも楽しいですよ
動かす
アニメーションをつけるときは、react-native-reanimated
を使います
タップできるようにする
View
からPressable
に変更し、onPress
でisChecked
の状態を切り替えられるようにしました
export default function Index() {
const [isChecked, setIsChecked] = useState(false);
return (
- <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
+ <Pressable style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }} onPress={() => setIsChecked(v => !v)}>
...
- </View>
+ </Pressable>
);
}
チェックが入ったときに、枠線が出てくるようにする
useSharedValue
をreanimatedからimportしてきます。
const progress = useSharedValue(0);
isChecked
がtrue
の場合に、progress
が1になるようにします。
useEffect(() => {
if (!isChecked) {
progress.value = withTiming(0);
return;
}
progress.value = withTiming(1);
}, [isChecked]);
Path
のend
にprogress
を指定します。
<Path path={rectPath} color="#3ea8ff" style="stroke" strokeWidth={strokeWidth} end={progress} strokeCap="round" />
strokeCap
にround
を指定するとこで、Pathの切り口が丸くなります
枠線のアニメーションプレビュー
全体のコード
export default function Index() {
const [isChecked, setIsChecked] = useState(false);
+ const progress = useSharedValue(0);
+ useEffect(() => {
+ if (!isChecked) {
+ progress.value = withTiming(0);
+ return;
+ }
+ progress.value = withTiming(1);
+ }, [isChecked]);
return (
<Pressable style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }} onPress={() => setIsChecked(v => !v)}>
<Canvas style={{ width: canvasWidth, height: canvasHeight }}>
<Path path={rectPath} color="#ffffff" />
- <Path path={rectPath} color="#3ea8ff" style="stroke" strokeWidth={strokeWidth} />
+ <Path path={rectPath} color="#3ea8ff" style="stroke" strokeWidth={strokeWidth} end={progress} strokeCap="round" />
<Path path={checkPath} color="#3ea8ff" style="stroke" strokeWidth={10} />
</Canvas>
</Pressable>
);
}
チェックマークにもアニメーションをつける
枠線と同様、end
とstrokeCap
をつけます。
strokeJoin
は折り返しの時に尖ってほしくないので、strokeCap
と同様にround
を指定します。
<Path path={checkPath} color="#3ea8ff" style="stroke" strokeWidth={10} end={progress} strokeCap="round" strokeJoin="round" />
完成
チェックマークにもアニメーションを設定したら完成です!
自分の場合は、背景もチェックと同じタイミングで背景色も変わるようにしてみました!
まとめ
React Native Skiaでチェックボックスを作ろう!って動き始めてから、このチェックボックスを完成させるまでに2時間もかかってしまいました...w
youtubeにReact Native Skiaの動画がたくさん上がってますが、結構クオリティが高いものばかりで、線の引き方や角丸の作り方などちょっとづつ参考になりそうなものを集めて...とやっていたら時間が経っていました。
僕みたいなちょっと触ってみたい!という方に届いたら嬉しいです!
Discussion