アニメーションには次の再生状態があります。今どの状態にあるかは、playStateプロパティで調べることができます。
idle
アニメーション設定の実行前で、アニメーションのタイム値が設定されていない状態。cancel()メソッドの呼び出しでplayStateはidleになる。
running
アニメーションが実行中の状態。play()、reverse()、Element.animate()メソッドの呼び出しでplayStateはrunningになる。
paused
アニメーション再生が一時停止された状態。pause()メソッドの呼び出しでplayStateはpausedになる。
finished
アニメーション再生が完了した状態。finish()メソッドの呼び出しは直ちにplayStateをfinishedにする。またplay()やreverse()、Element.animate()の呼び出して再生が完了したときにもfinishedになる。
次の例は、雲に乗ったカミナリ小僧が、ボタンのクリックでアニメーションを始め、雲の移動アニメーションが終わったときに、雲から落ちて、地面でバウンドするアニメーションです。
HTML
<div class="container">
<div class="cloud">
<img src="images/cloud.png">
<div class="kaminari"></div>
</div>
</div>
<input type="button" id="play-btn" value="Cloud Move">
CSS
.container {
position: absolute;
top: 10px;
left: 10px;
width: 10px;
height: 10px;
/* border: 1px solid black; */
}
.cloud {
position: absolute;
}
.kaminari {
position: absolute;
width: 65px;
height: 84px;
background: url(images/kaminari.png) no-repeat;
background-position: center bottom;
top: 10px;
left: 10px;
}
#play-btn {
position: absolute;
top: 300px;
}
JavaScript
// 雲が右へ進みながら上下するアニメーション
// CSS Animationsと同様、親要素に右に進むアニメーションを設定し、
// 子要素に上下するアニメーション設定している、
// 共通
const timings = {
duration: 3000,
iterations: 1,
fill: 'forwards',
easing: 'ease-in'
};
// 親の右へ移動するアニメーション
const container = document.querySelector('.container');
const keyframesContainer = {
transform: ['translateX(0)', 'translateX(500px)']
};
const effectContainer = new KeyframeEffect(container, keyframesContainer, timings);
const animContainer = new Animation(effectContainer, document.timeline);
// 雲が上下するアニメーション
const cloud = document.querySelector('.cloud');
const keyframesCloud = {
transform: ['translateY(0)', 'translateY(200px)', 'translateY(0)']
};
const effectCloud = new KeyframeEffect(cloud, keyframesCloud, timings);
const animCloud = new Animation(effectCloud, document.timeline);
// 雲のアニメーションが終了したら、制御はJavaScriptに移る
animCloud.addEventListener('finish', () => {
render()
}, false);
// [Cloud Move]ボタンのクリックで、
document.getElementById('play-btn').addEventListener('click', () => {
// 親と雲のアニメーションを開始
animContainer.play();
animCloud.play();
// 次回のクリックでも同様にアニメーションさせる場合には、
// カミナリ小僧のtransformとy位置の変数を元に戻す
kaminari.style.transform = 'translateY(0)';
ypos = 10;
}, false);
// カミナリ小僧のアニメーション。
const kaminari = document.querySelector('.kaminari');
const windowHeight = window.innerHeight;
const windowWidth = window.innerWidth;
const kaminariHeight = 84;
kaminari.style.left = "10px";
kaminari.style.top = "10px";
let ypos = 10;
let ay = 0;
const R = kaminariHeight;
const GRAVITY = 9.8;
const FRICTION = 0.85;
// 調整
const ground = windowHeight - 24;
const render = () => {
// 雲のアニメーションが再生中は何もしない
if (animCloud.playState === 'running') {
return;
}
update();
kaminari.style.transform = 'translateY(' + ypos + 'px)';
window.requestAnimationFrame(render);
}
const update = () => {
ay += GRAVITY;
ypos += ay;
if (ypos > ground - R) {
ay *= -1
ay *= FRICTION;
ypos = ground - R;
if (Math.abs(ay) < GRAVITY) {
ay = 0;
}
}
}
これは、Web Animations APIと、JavaScriptのrequestAnimationFrame()を組み合わせた例で、まずWeb Animations APIアニメーションを実行し、実行中は何もせず、playStateがrunningでなくなったら、直ちにrequestAnimationFrame()で要素をアニメーションする、という方法を取っています。
const render = () => {
// 雲のアニメーションが再生中は何もしない
if (animCloud.playState === 'running') {
return;
}
update();
kaminari.style.transform = 'translateY(' + ypos + 'px)';
window.requestAnimationFrame(render);
}