admin管理员组

文章数量:1405636

I do know about the case of float/integer values for drawImage's x and y. But what I need is a smooth animation with an ability to cache my shapes.

  • Article on caching plex paths with backup canvas
  • Article on drawImage with float parameters

For example, I want to draw some plex shape (i.e. SVG-tiger, converted to canvas-mands) to canvas just once and then move it smoothly with ctx.translate and ctx.drawImage. I need the float values then, because instead I get a step-by-step moving:

Here's the examples at JSFiddle:

  • One: Fast speed, with Math.floor applied to translate parameters (x and y are equal to time in seconds multiplied by 10): Animation is weird (sequential, not smooth).
  • Two: Slow speed, with Math.floor applied to translate parameters (x and y are equal to time in seconds): Animation is weird (sequential, not smooth).
  • Three: Fast speed, no rounding, float values (x and y are equal to time in seconds multiplied by 10). Speed is fast, so animation looks good.
  • Four: Slow speed, no rounding, float values (x and y are equal to time in seconds). Speed is slow, so animation looks pulsating. Why?

The last case is the one that confuses me. Am I wrong in my tryings and there is a possibility to make this caching trick work nice?

In Firefox, there is a property of canvas named mozImageSmoothingEnabled (see), but there is no help from that in other browsers. And it also removes paths smoothing.

Code extract:

var shapeCanvas = null;
var w = 320, h = 240;
var startTime = 0;

function start() {
    startTime = Date.now();
    var docCanvas = document.getElementById('canvas');
    . . .
    shapeCanvas = document.createElement('canvas');
    . . .
    drawShape(shapeCanvas.getContext('2d'));

    drawNext(docCanvas.getContext('2d'));
}

function drawNext(ctx) {
    var msec = (Date.now() - startTime);
    var time = msec / 1000; // seconds passed from start
    ctx.clearRect(0, 0, w, h);

    ctx.save();
    // the lines to change: time | (time * 10) | Math.floor(time * 10)
    ctx.translate((time < 500) ? Math.floor(time * 10) : 500,
                  (time < 500) ? Math.floor(time * 10) : 500);
    ctx.drawImage(shapeCanvas, 0, 0);
    ctx.restore();

    __nextFrame(function() {
       drawNext(ctx);
    });
}

function drawShape(ctx) {
    . . .
}

I do know about the case of float/integer values for drawImage's x and y. But what I need is a smooth animation with an ability to cache my shapes.

  • Article on caching plex paths with backup canvas
  • Article on drawImage with float parameters

For example, I want to draw some plex shape (i.e. SVG-tiger, converted to canvas-mands) to canvas just once and then move it smoothly with ctx.translate and ctx.drawImage. I need the float values then, because instead I get a step-by-step moving:

Here's the examples at JSFiddle:

  • One: Fast speed, with Math.floor applied to translate parameters (x and y are equal to time in seconds multiplied by 10): Animation is weird (sequential, not smooth).
  • Two: Slow speed, with Math.floor applied to translate parameters (x and y are equal to time in seconds): Animation is weird (sequential, not smooth).
  • Three: Fast speed, no rounding, float values (x and y are equal to time in seconds multiplied by 10). Speed is fast, so animation looks good.
  • Four: Slow speed, no rounding, float values (x and y are equal to time in seconds). Speed is slow, so animation looks pulsating. Why?

The last case is the one that confuses me. Am I wrong in my tryings and there is a possibility to make this caching trick work nice?

In Firefox, there is a property of canvas named mozImageSmoothingEnabled (see), but there is no help from that in other browsers. And it also removes paths smoothing.

Code extract:

var shapeCanvas = null;
var w = 320, h = 240;
var startTime = 0;

function start() {
    startTime = Date.now();
    var docCanvas = document.getElementById('canvas');
    . . .
    shapeCanvas = document.createElement('canvas');
    . . .
    drawShape(shapeCanvas.getContext('2d'));

    drawNext(docCanvas.getContext('2d'));
}

function drawNext(ctx) {
    var msec = (Date.now() - startTime);
    var time = msec / 1000; // seconds passed from start
    ctx.clearRect(0, 0, w, h);

    ctx.save();
    // the lines to change: time | (time * 10) | Math.floor(time * 10)
    ctx.translate((time < 500) ? Math.floor(time * 10) : 500,
                  (time < 500) ? Math.floor(time * 10) : 500);
    ctx.drawImage(shapeCanvas, 0, 0);
    ctx.restore();

    __nextFrame(function() {
       drawNext(ctx);
    });
}

function drawShape(ctx) {
    . . .
}
Share Improve this question edited Nov 27, 2020 at 17:36 C. Tewalt 2,5072 gold badges32 silver badges52 bronze badges asked Dec 7, 2011 at 9:15 shaman.sirshaman.sir 3,3083 gold badges30 silver badges36 bronze badges 1
  • 1 The path is rendered on shapeCanvas with anti-aliasing. When you draw an image with antialiasing onto a canvas at a non-integer location, you get anti-aliasing on top of anti-aliasing. The pulsating blurry edge of the stroke bees thickest when drawing the image halfway between integer pixel locations and vanishes when drawing the image exactly on integer pixel locations. – ellisbben Commented Feb 1, 2012 at 21:52
Add a ment  | 

2 Answers 2

Reset to default 4 +50

I wrote the tutorial in your first link.

Just to clear the air:

shapeCanvas.style.width = w + 'px';
shapeCanvas.style.height = h + 'px';

is not really worth doing. No point setting the style if its just a in-memory canvas, and you shouldn't really ever want to set the width and height style of a canvas anyway, it just confounds things.

What ellisbben said in the ment is pretty much what's happening.

It's possible to get around it in a few hackish ways I bet. One way might be to make sure its never drawn on an integer pixel. Another might be to use ctx.scale(.99,.99) before drawing anything so it is always anti-aliased. It's tough to get a consistent solution here because different browswer's implementations of anti-aliasing are different.

Here are a few experiments from myself:

http://jsfiddle/KYZYT/29/

The first two are the shape drawn from a canvas and also drawn from a PNG

The second two are the same pair but scaled by .99,.99

The last one is the real thing. It still blurs a bit but looks a lot sharper than using the images.

None of my experiments lead to an end of your pulsating, at least not on a microscopic level. I think this is just something you're going to have to live with if you want to animate pixel-perfect images onto half-pixel spaces.

If you really feel you can't just draw on perfect pixels then your (second) best bet for consistency is probably to find a way to force anti-aliasing at all times. Making sure you are always translated to a non-integer or scaling it ever so slightly are decent candidates, but there may be others.

To be honest, you best bet is to not cache these animated paths until you absolutely need the performance from them. Cache the stylized buttons and other static things you make, but if you've got animated paths that need to move slowly and precisely and look very good, you might as well stick with the true thing over my caching optimization unless you really need it for those too.

Bit shameless plug but: I've implement smooth animation in HTML5 slot game with bit hacky way. The generated cached Image is drawn on small canvas once and then I used translate3d() with -moz-transform / -webkit-transform styles for the canvas to move, mirror and scale the image around.

Pregeneration

  1. Create Image
  2. Draw image content
  3. Create canvas object in DOM

Animation phase

  1. Clear canvas
  2. Draw the cached image to the canvas
  3. Use CSS3 transforms (scale3d and translate3d) to move canvas around.

本文标签: