javascript - HTML5 Canvas: Element som ritas med/cachade för drawImage blir mjukade när de skalas och/eller flyttas


Jag vet om fallet med flyttal/heltal för drawImage's x och y. Men det jag behöver är en smidig animering med möjlighet att cachea mina former.

  • Artikel om cachning av komplexa vägar med reservcanvas
  • Artikel om drawImage med flyttalparametrar

Till exempel, jag vill rita någon komplex form (dvs. SVG-tiger, konverterad till canvas-kommandon) på canvas bara en gång och sedan flytta den smidigt med ctx.translate och ctx.drawImage. Jag behöver flyttal då, eftersom jag istället får en steg-för-steg-rörelse:

Här är exemplen på JSFiddle:

  • Ett: Snabb hastighet, med Math.floor applicerad på translate-parametrar (x och y är lika med tid i sekunder multiplicerat med 10): Animationen är konstig (sekventiell, inte smidig).
  • Två: Låg hastighet, med Math.floor tillämpad på translate-parametrarna (x och y är lika med tiden i sekunder): Animationen är konstig (sekventiell, inte smidig).
  • Tre: Snabb hastighet, ingen avrundning, flyttal (x och y är lika med tiden i sekunder multiplicerat med 10). Hastigheten är snabb, så animationen ser bra ut.
  • Fyra: Låg hastighet, ingen avrundning, flyttal (x och y är lika med tid i sekunder). Hastigheten är låg, så animationen ser pulsande ut. Varför?

Det sista fallet är det som förvirrar mig. Har jag fel i mina försök och finns det en möjlighet att få denna cachningstrick att fungera bra?

I Firefox finns det en egenskap för canvas som heter mozImageSmoothingEnabled (se), men det finns ingen hjälp av det i andra webbläsare. Och det tar också bort kurvsmoothing.

Kodutdrag:

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) {
    . . .
}

Svar #1

Jag skrev handledningen i din första länk.

Bara för att reda ut luften:

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

är egentligen inte värt att göra. Det är meningslöst att sätta stilen om det bara är en canvas i minnet, och du borde egentligen aldrig vilja sätta bredden och höjden stil på en canvas ändå, det förvirrar bara saker.

Det ellisbben sa i kommentaren är ganska mycket vad som händer.

Det är möjligt att kringgå det på några hackiga sätt, skulle jag tro. Ett sätt kan vara att se till att det aldrig ritas på en heltalspixel. Ett annat kan vara att använda ctx.scale(.99,.99) innan man ritar något så att det alltid blir anti-aliasat. Det är svårt att få en konsekvent lösning här eftersom olika webbläsares implementationer av anti-aliasing är olika.

Här är några experiment från mig själv:

http://jsfiddle.net/KYZYT/29/

De två första är formen som ritats från en duk och även ritats från en PNG

De två andra är samma par men skalade med .99,.99

Den sista är den verkliga saken. Den är fortfarande lite suddig men ser mycket skarpare ut än när man använder bilderna.

Inga av mina experiment leder till ett slut på ditt pulserande, åtminstone inte på mikroskopisk nivå. Jag tror att det här är något du helt enkelt måste leva med om du vill animera pixelperfekta bilder på halv-pixel-utrymmen.

Om du verkligen känner att du inte bara kan rita på perfekta pixlar, då är ditt (näst) bästa alternativ för konsekvens förmodligen att hitta ett sätt att tvinga fram anti-aliasing hela tiden. Att se till att du alltid översätts till ett icke-heltal eller skala det lite grann är bra kandidater, men det kan finnas andra.

Ärligt talat är ditt bästa alternativ att inte cachelagra dessa animerade banor förrän du verkligen behöver prestandan från dem. Cachelagra de stiliserade knapparna och andra statiska saker du gör, men om du har animerade banor som behöver röra sig långsamt och exakt och se väldigt bra ut, kan du lika gärna hålla dig till det riktiga istället för min cacheoptimering om du verkligen inte behöver det för dem också.

Svar #2

Lite skamlös reklam men: jag har implementerat smidig animation i HTML5 slotspel på ett lite hackigt sätt. Den genererade cachade bilden ritas på en liten canvas en gång och sedan använde jag translate3d() med -moz-transform/-webkit-transform stilar för canvasen för att flytta, spegla och skala bilden runt.

Förgenerering

  1. Skapa bild
  2. Rita bildinnehåll
  3. Skapa canvasobjekt i DOM

Animationsfas

  1. Rensa duk
  2. Rita den cachade bilden på duken
  3. Använd CSS3-transformeringar (scale3d och translate3d) för att flytta canvas runt.