10_6 アニメーションの再生状態

アニメーションには次の再生状態があります。今どの状態にあるかは、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);
}

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA