anime.jsでシークバーに連動する数値アニメーションを実装した
前書き
作りたいものの説明
私は今、生産シミュレーションアプリを作成している。生産設備を配置しそれぞれを線で結ぶことで、生産ラインを形成する。ラインの中を人が行き来して、製品をひとつずつ作り上げる様をシミュレーションする。
解決したかったこと
シミュレーションの機能として再生や停止機能を設けた。シークバーを設けて任意の位置から再生できるようにした。このほかにも現在の生産数を表示させるようにしたい。シークバーに連動して、その値が動くようにする。
そのやり方がわかったので、ここで説明する。
anime.jsとは何か
軽量なJSアニメーションライブラリ。CSSやDOM、JSオブジェクトを対象にしてアニメーションを作成することができる。実装も簡単で、ぬるぬる動かせる。良い。
これを用いて数値をアニメーションにしたい。
シークバーの実装
animeインスタンスの説明
animeインスタンスを作成し、その戻り値を使用することで、アニメーションをつくることができる。
let animation = anime({
targets: '#ob1',
translateX: 300,
easing: "linear",
duration: 4000
}
こうすると対象となる要素が右に300px移動する。変化の仕方や変化時間なども指定できる。
タイムラインの概要
複数のanimeインスタンスをひとつのタイムラインに載せることで、各種アニメーションをひとつの時間軸上で表現できる。動画を繋ぎ合わせる作業に似ている。
シークバーで連動するアニメーション
シークバーはinputタグのtypeオプションをrangeに変更することで表現できる。そして、その値が変わった時のイベントを設定する。
<input class="progress" step=".001" min="0" max="100" value="0" type="range"></input>
let controlsProgress = document.querySelector("input.progress");
let tl = anime.timeline({
easing: "linear",
autoplay: false,
update: function () {
controlsProgress.value = tl.progress;
},
});
controlsProgress.oninput = function () {
tl.seek(tl.duration * (controlsProgress.value / 100));
};
update
とは対象が更新された時にコールバック関数を呼び出す方式。
seekメソッドはtimeline上の任意の時刻の状態を呼び出す。シークバーを動かすと、その時点の描画状態が表現される。
対象を数値にすると動かない
SVGオブジェクトは問題なくアニメーションさせることができた。しかし、生産数というJSオブジェクトを対象にするとうまく動かない。
はじめはこのサンプルを参考にした。コールバックを呼び出す条件をupdateとして、それをタイムライン上に表現した。
<div id="count1">生産数</div>
// 先ほどのコードからの続き
function getCountObjectByUpdate(countSize) {
let count = {
"生産数(update)": countSize,
};
let JSobjectProp = anime({
targets: count,
totalCount: countSize,
easing: "linear",
autoplay: false,
round: 1,
value: countSize,
update: function () {
var el = document.querySelector("#count1");
el.innerHTML = JSON.stringify(count);
},
});
return JSobjectProp;
}
tl.add(getCountObjectByUpdate(0),0);
tl.add(getCountObjectByUpdate(1),1000);
tl.add(getCountObjectByUpdate(2),2000);
tl.add(getCountObjectByUpdate(3),3000);
tl.add
とすることで、タイムラインにアニメーションを追加させる。第二引数はオフセット値である。タイムラインの挿入場所を今回は絶対値で指定している。単位はミリ秒である。
このように実装すると、シークバーを動かした際に、数値が正しく変化しない。はじめに正方向に動かすと順調に生産数が増加するか、逆方向に動かすといきなり0になる。どういうことだ。
こうしたら動いた
updateではなくchangeを使う。
let JSobjectProp = anime({
targets: count,
totalCount: countSize,
easing: "linear",
autoplay: false,
round: 1,
value: countSize,
change: function () {
var el = document.querySelector("#count2");
el.innerHTML = JSON.stringify(count);
},
});
なぜできたのか
change
は、プロパティの値が変更されたタイミングで呼び出されるコールバックである。そのため、シークバーを移動させたときにも適切に値が更新され、意図した動作になったと考えられる。
update
の説明は、ドキュメントには以下のように書いてある。
Callback triggered on every frame as soon as the animation starts playing.
アニメーションが開始されてから毎フレームごとに呼び出すとある。しかし、これは説明と動作が異なる可能性がある。
現に、こういうIssueも挙げられている。
原因はよくわからない。考えられるとすればコールバックのタイミングがドキュメントの説明とは異なるのか。実際は毎フレームではなく、内部状態から比較して変異があれば動作する、ということなのだろうか。可能性の一つとして考えられる。
想定される動きではないことは確かである。
コードのまとめ
全体のコードを以下に載せておく。
Discussion