質問

I am planning to play a short animation by quickly showing several images one after another. The images will be the frames of animation. I want to have 60 fps. Of course, the images will be preloaded. There are some reasons why I don't want to use a <video> tag.

As for me, there are 2 options:

  • change src tag of an image tag
  • display images inside <canvas> as described here

Before writing complicated benchmarks for all browsers I'm asking those who have any experience:

  1. Which is the best way to do it?
  2. Are there any pitfalls of these methods or ways to speed it up?
役に立ちましたか?

解決

The solution you want is the canvas.
Only with it you'll have the (easy) possibility to use one single -or a reduced set- of graphic files that you'll use for your animations.
Since you can't afford to have a thousand files to download you'll in fact need to handle packed files, which may look like :

enter image description here

Several characters or any graphic can be packed in such file.

Once you loaded the image, you have to compute the texture coordinates for each frame.
An animation is a sequence of frame.
The key method of the canvas here is drawImage, which comes in a 9 arguments flavor that allows to clip/resize/... the image.

I've done a little fiddle to illustrate, but you should seriously consider using a framework for the animation, it might provide you most valuable tool (animation transition, to quote one).

fiddle here : http://jsfiddle.net/gamealchemist/Vn2Nt/

// array containig texture coords of all frames.
var textureCoords = [];

// fill the textureCoords using image img, assuming
//   frames are tiled in a 'regular way', sized tw X th, 
//   uses only cnt frames.
function fillTextureCoords(img, tw, th, cnt) {
    var w = img.width,
        h = img.height;
    var hCount = Math.floor(w / tw),
        vCount = Math.floor(h / th);
    for (var vIndex = 0; vIndex < vCount; vIndex++)
    for (var hIndex = 0; hIndex < hCount; hIndex++) {
        textureCoords.push({
            u: hIndex * tw,
            v: vIndex * th,
            w: tw,
            h: th
        });
        cnt--;
        if (!cnt) return;
    }
}

// frames of the animation. 
// Contains index to the textureCoords array.
var frames = [];

for (var i = 0; i < 14; i++) frames.push(i);

// duration of a single frame.
var frameDuration = 150;

// current time of the animation (ms).
var animationStatus = 0;

// draws at (0,0) the current animation frame
function drawThatFrame() {
    var whichFrame = Math.floor(animationStatus / frameDuration);
    whichFrame %= frames.length;
    var tCoord = textureCoords[frames[whichFrame]];
    context.drawImage(myBird, tCoord.u, tCoord.v, tCoord.w, tCoord.h, 0, 0, tCoord.w, tCoord.h);
}

他のヒント

I would advise displaying the images in a canvas as suggested in your second option. The reason for this is that while you can make sure images are preloaded in the DOM, you can't control when they are decoded from source into image data — typically this will happen the moment they are made visible in the document, and this can (and does!) cause an execution lag which will impact your framerate. Using canvas you can also pre-decode the image data, resulting in a smoother animation.

I recently ended up using his technique when some slide-show-like software I was writing for kiosks exhibited a nasty drop in frame rate (60 FPS to 20 FPS) when images that had been preloaded first slid into view: using Chrome's Dev Tools' Timeline functionality I was able to isolate the image decoding step as locking UI for 50-100 milliseconds, and I managed to completely obviate this by decoding the images ahead of time using canvas.

I would put all images on top of each other with position:absolute; inside a div and then use the jQuery each function to hide the images sequencialy.

Use something like:

// this is a useful shim for requestAnimationFrame. I copied it from somewhere else:
window.requestAnimFrame = function(){
    return (
        window.requestAnimationFrame       ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame    ||
        window.oRequestAnimationFrame      ||
        window.msRequestAnimationFrame     ||
        function(/* function */ callback){
            window.setTimeout(callback, 1000 / 60);
        }
    );
}();

var currentFrameTime, previousFrameTime, timeBetweenFrames;
var playbackStartTime = Date.now();
var timePerFrame = 1000/60.0;

function updateFrame(frameNumber) {
    requestAnimFrame(updateFrame);

    currentFrameNumber = parseInt((currentFrameTime - playbackStartTime) / timePerFrame);

    showImage(frameNumber);
}

It'll show up to 60 frames per second, but won't play too fast or too slow.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top