Time-Based Animation (css example)

http://viget.com/inspire/time-based-animation

by jaibuu

HTML

<script src="http://danieltello.com/demos/time-based-animation/javascripts/requestAnimationFrame.js"></script>
<div id="css-demo">
    <div class="buttons">
        <label for="speed">Speed</label>
        <input name="speed" id="speed-input" type="number" value="200"> px/sec</input>
        <button onclick="APP.play();">Play</button>
        <button onclick="APP.pause();">Pause</button>
        <span class="framerate"><span id="framerate">0</span> fps</span>
    </div>

    <div id="css-bullet"></div>
    <div id="css-cannon"></div>
</div>

CSS

body, html {
    font-family: sans-serif;
    font-size: 14px;
    margin: 0;
    padding: 0;
}

#css-demo {
    background: #9adaea url(http://viget.com/uploads/file/time-based-animation/images/ground.png) repeat-x 0 100%;
}

#css-demo {
    height: 338px;
    max-width: 580px;
    overflow: hidden;
    position: relative;
    width: 100%;
}

#css-bullet {
    background: url(http://viget.com/uploads/file/time-based-animation/images/bullet-bill.png) no-repeat;
    height: 56px;
    left: 460px;
    position: absolute;
    top: 114px;
    width: 64px;
}

#css-cannon {
    background: url(http://viget.com/uploads/file/time-based-animation/images/cannon.png) no-repeat;
    height: 128px;
    right: 56px;
    position: absolute;
    top: 110px;
    width: 64px;
}

#speed-input {
    width: 60px;
}

.buttons {
    -webkit-font-smoothing: antialiased;
    background: #201400;
    background: rgba(0,0,0,0.8);
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    -ms-box-sizing: border-box;
    box-sizing: border-box;
    color: #fff;
    font-weight: bold;
    height: 50px;
    line-height: 50px;
    padding: 0 10px;
    position: absolute;
    top: 288px;
    width: 100%;
    z-index: 1;
}

.framerate {
    float: right;
}

JavaScript

// Requires requestAnimationFrame polyfill
// https://gist.github.com/1579671

window.APP = window.APP || {};

APP.init = function() {
    APP.setup.initObjects();
    APP.setup.initSpeedInput();
    APP.setup.addListeners();
    APP.play();
};

APP.pause = function() {
    window.cancelAnimationFrame(APP.animationFrameLoop);
    APP.isRunning = false;
};

APP.play = function() {
    if(!APP.isRunning) {
        APP._then = Date.now();
        APP.core.frame();
        APP.isRunning = true;
    }
};

APP.core = {
    frame: function() {
        APP.now = Date.now();
        APP._delta = (APP.now - APP._then) / 1000; // Converts to seconds (optional)
        APP._then = APP.now;
        APP.core.update();
        APP.animationFrameLoop = window.requestAnimationFrame(APP.core.frame);
    },

    update: function() {
        APP.workers.moveObjects();

        // Update css position
        var wholePixelBulletX = (APP.bullet.x + 0.5) | 0;  // Fast round to whole pixel
        APP.bullet.element.style.left = wholePixelBulletX + 'px';

        // Render Framerate every 1/4 second
        if(APP.framerateDisplay.timer > 0.25) {
            APP.framerateDisplay.innerHTML = (1/APP._delta) | 0; // fast round to whole number
            APP.framerateDisplay.timer = 0;
        } else {
            APP.framerateDisplay.timer += APP._delta;
        }
    }
};

APP.setup = {
    addListeners: function() {
        // Makes demo responsive for small screens
        window.addEventListener('resize', APP.workers.calculateBulletStart);
    },

    initSpeedInput: function() {
        APP.speedInput = document.getElementById('speed-input');
        APP.speedInput.value = APP.bullet.speed;
        APP.speedInput.addEventListener('keyup', APP.workers.updateSpeed);
        APP.speedInput.addEventListener('change', APP.workers.updateSpeed);
    },

    initObjects: function() {
        APP.bullet = {
            element: document.getElementById('css-bullet'),
            x: 460,
      ...