2022-08-10

Skiaで放物線の動きを描く

放物線の動きサンプル

Skiaで物体が放物線の動きを描いて移動していくサンプルです。"RUN"ボタンを押すと実行されます。

Loading Skia..

実装方法

静的な放物線を描画するだけであれば<Path />の曲線を使えばよさそうです。ただ動的に位置を移動させるとなると各移動位置の座標が必要そうなため、三角関数のsinカーブで放物線の座標を計算することにしました。X軸がθ(角度)でY軸がsinθのグラフの場合、θが0〜180の場合に上向きの放物線になるため、それを利用します。

以下でXが0からπ(180°)までの場合のY座標の位置をそれぞれ取得し配列にしておきます(100分割してます)。

  const curveYPath = useMemo(() => {
    const curveHeight = canvasSize.height - 200

    return range(0, 100)
      .map(i => (Math.PI / 100) * i)
      .map(theta => {
        const sin = Math.sin(theta) * curveHeight
        return -sin
      })
  }, [])

-sinしているのは、CanvasではYが上から下に向けて値が大きくなっていたため、縦位置をマイナスにする必要があるためです。

動的に変化する値としてposというvalueを用意しておき、

const pos = useValue(0)

移動させるタイミングでposの値を0からMath.PI(180°)に変化させます。

runTiming(pos, Math.PI, { duration: 3000 })

Canvasで動かしたいシェイプを囲んだ<Group>に対しtranslateXtranslateYを指定します。interpolateposの値を元に移動量を割り当てます。 translateXposが増えると等分に横に移動していき、translateYは作成しておいたY座標位置の配列を元に移動させます。

  return (
    <Canvas style={{ flex: 1 }}>
      <Group
        transform={useComputedValue(() => {
          return [
            {
              translateX: interpolate(
                pos.current,
                [0, Math.PI],
                [0, canvasSize.width - ballRadius * 2],
              ),
            },
            {
              translateY: interpolate(
                pos.current,
                range(0, 100).map(i => (Math.PI / 100) * i),
                curveYPath,
              ),
            },
          ]
        }, [pos])}
      >
        <Circle
          r={ballRadius}
          cx={startPos.x}
          cy={startPos.y}
          color="lightblue"
          style="fill"
        />
      </Group>
    </Canvas>
  )

全体のコードは以下で確認できます。

https://github.com/gaishimo/omoidasu-tech-blog/blob/main/components/pages/2022-08-10-parabola-move/ParabolaMove.tsx

最終更新: 2022-08-14 01:11
筆者: @gaishimo 主にReact Nativeでのアプリ開発を行っています。
© 2021 Omoidasu, Inc. All rights reserved.