Quickstart
このガイドでは、Anisketchを使って簡単なアニメーションを作成する方法を説明します。
セットアップ
Anisketchを使うためにはNode.jsをインストールする必要があります。Node.jsをインストールされているかどうかは、次のコマンドで確認できます。
node -v加えて、pnpmをインストールすることをお勧めします。pnpmはNode.jsのパッケージマネージャーで、npmやyarnと同様の機能を提供します。pnpmを使うことで、パッケージのインストールが高速になり、ディスクの容量も節約できます。
プロジェクトの作成
Anisketchプロジェクトを作成するには、次のコマンドを実行します。
pnpm create anisketchこのコマンドを実行すると、Anisketchプロジェクトに必要なファイルが生成され、依存関係がインストールされます。
エディターを起動するためには、次のコマンドを実行します。
-
作成したプロジェクトのディレクトリに移動します。
Terminal window cd my-anisketch-project -
エディターを起動します。
Terminal window pnpm start
これで、Anisketchのエディターが起動し、アニメーションを作成する準備が整いました。
初めてのアニメーションを作成する
主要な概念を説明するため、まずはGUIを使わずにアニメーションを作成する方法を説明します。
コードエディターを開いて src/main.anim.tsx を作成し、次のコードを追加します。
import * from 'anisketch'
export default defineAnimation({ background: 'transparent', size: { width: 1920, height: 1080 }, frameRate: 60, action: thread(async () => { const scene = currentScene() const circle = <Circle x={100} y={100} radius={50} key={key()} />
scene.add(circle)
await wait(1000) for (let i = 0; i < 60; i++) { circle.x += 5 await waitFrame() }
await wait(1000) scene.remove(circle) }),})ファイルを保存すると、先程起動したエディターに自動的に更新が反映されます。
円が右に移動するアニメーションが再生されれば成功です!
GUI上で編集する
先ほど作成したアニメーションをGUIを使って編集することもできます。
エディター上で円を選択し、左側のプロパティパネルからradiusやyの値を変更してみましょう。円の大きさや位置が変わると共に、コードも自動的に書き換わるはずです。
次に、xの値を変更してみてください。アニメーション開始時刻では値を書き換えることができますが、円が移動し始めた後では値を変更することができないはずです。これはcircle.x += 5 が実行された結果を目的の値とするために、古いcircle.xの値を書き換えるべきか、あるいは右辺の5を書き換えるべきかを判断できないためです。これに限らず、AnisketchのGUI上での編集には一部の制約があります。
解説
Anisketchでアニメーションを作成するために、次の概念が登場しました。
animation- アニメーションの処理を表す
thread、そしてサイズや背景色などの追加の情報が含まれます。 - 定義するためには
defineAnimation({ ... })のように記述します。
- アニメーションの処理を表す
thread- アニメーションの時間経過に伴う処理です。
- 定義するためには
thread(async () => { ... })のように記述します。 thread内ではwait()やwaitFrame()などの関数を使って、アニメーション時間の経過を待つことができます。
scene- 表示されているコンポーネントをまとめて管理します。
- コンポーネントは
sceneに追加されるまで表示されません。
- コンポーネント
- アニメーションの対象となる図形やテキストなどを表します。
<Circle>や<Text>など多くのコンポーネントが用意されています。
基本的には、コンポーネントをsceneに追加したり編集したりするthreadとしてアニメーションを定義し、それだけでは不足している情報をanimationで補完するという流れになります。
GUIでアニメーションを作成する
GUIを使って先ほどと同じアニメーションを作成してみましょう。次のようなコードが生成されるはずです。
import * from 'anisketch'
export default scene({ background: 'transparent', size: { width: 1920, height: 1080 }, frameRate: 60, action: thread(async ({ scene }) => { schedule({ start: 0, action: thread(async () => { scene.add(<Circle x={100} y={100} radius={50} key={key('kodane')} />) }), })
schedule({ start: 1000, action: thread(async () => { const target = scene.get('kodane', 0) await tween(target.x, 300, 1) }), })
schedule({ start: 2000, action: thread(async () => { const target = scene.get('kodane', 0) scene.remove(target) }), }) }),})先ほどと同じアニメーションですが、GUIを使って作成したコードは先程手書きしたコードとは少し異なっています。GUIを使ってアニメーションを作成すると、GUI上での編集とコード書き換えが単純でわかりやすい対応関係を持つようにコードが生成されるためです。
解説
1. schedule() 関数
GUIを使ってアニメーションを作成すると、schedule()という関数が登場します。
この関数は、threadの実行が時間軸上の特定の時点で開始されるようにスケジュールするためのものです。先に示したwait()を使って時間経過を待つ場合と異なり、アニメーションを開始する絶対時間を指定することができます。
wait()を使う場合にはアニメーションを開始する順番を変えるためにはコードを書く順番を入れ替える必要がありますが、schedule()を使うと開始時刻の指定を変えるだけで順番を変更できます。これはアニメーションの開始時刻をGUI上で変更するために都合の良い挙動です。
schedule()でスケジュールされたスレッドの開始タイミングはGUI上で編集することができます。特に理由がない場合には、schedule()を使ってアニメーションを作成することをお勧めします。
Code Example
次に示すコードは、いずれも同様の挙動を表しています。
schedule({ start: 1000, action: thread(async () => console.log('1 second passed')),})schedule({ start: 2000, action: thread(async () => console.log('2 seconds passed')),})// Start 2 threads simultaneouslythread(async () => { await wait(1000) console.log('1 second passed')})thread(async () => { await wait(2000) console.log('2 seconds passed')})await wait(1000)console.log('1 second passed')await wait(1000)console.log('2 seconds passed')2. scene.get() 関数
scene.get()関数は、sceneに追加されたコンポーネントをkeyで取得するための関数です。
同じkeyを持つコンポーネントが複数ある場合があるため、第2引数にインデックスを指定してどのコンポーネントを取得するのかを特定します。
手書きのコードでは<Circle>のインスタンスをcircleという変数に代入していましたが、GUIを使って作成したコードではどのような変数名をつけることが適切なのかエディタには分かりません。そのため、変数を介さずにscene.get()を使ってコンポーネントを取得するコードが生成されます。
3. tween() 関数
tween(mutable, target, duration)関数は、mutableの値をtargetにduration秒かけて変化させる async 関数です。
この関数は通常の関数ではなく、マクロの一つです。
tween(target.x, 300, 1)というコードは、トランスパイル時にtween(x => target.x = x, 300, 1)のようなコードへ自動的に変換されます。このような変換が行われることで、アニメーションの記述を簡潔に保つことができます。
おすすめのワークフロー
基本的には、GUIを使ってアニメーションを作成し、高度なアニメーションが必要な場合には schedule() でスケジュールされる処理として複雑なロジックを書くことをお勧めします。
あるいは、カスタムコンポーネントを定義することも検討してみてください。