javascript - HTML5 Canvas: Elementer tegnet med/bufret for drawImage blir glattet når de skaleres og/eller flyttes
Jeg vet om saken med flyt-/heltallverdier for drawImage's x og y. Men det jeg trenger er en jevn animasjon med mulighet til å cache mine former.
- Artikkel om caching av komplekse stier med reserve lerret
- Artikkel om
drawImagemed flyttallsparametere
For eksempel, jeg vil tegne noen komplekse former (f.eks. SVG-tiger, konvertert til canvas-kommandoer) til canvas bare én gang og deretter flytte den jevnt med ctx.translate og ctx.drawImage. Jeg trenger da flyttallsverdiene, fordi jeg ellers får en trinnvis bevegelse:
Her er eksemplene på JSFiddle:
- Én: Rask hastighet, med
Math.floorbrukt påtranslate-parametrene (xogyer lik tiden i sekunder multiplisert med10): Animajsonen er merkelig (sekvensiell, ikke jevn). - To: Lav hastighet, med
Math.flooranvendt påtranslate-parametere (xogyer lik tiden i sekunder): Animasjonen er rar (sekvensiell, ikke jevn). - Tre: Rask hastighet, ingen avrunding, flyttallverdier (
xogyer lik tiden i sekunder multiplisert med10). Hastigheten er rask, så animasjonen ser bra ut. - Fire: Lav hastighet, ingen avrunding, flyttallverdier (
xogyer lik tid i sekunder). Hastigheten er lav, så animasjonen ser pulserende ut. Hvorfor?
Det siste tilfellet er det som forvirrer meg. Tar jeg feil i mine forsøk, og er det en mulighet for å få dette caching-trikset til å fungere bra?
I Firefox finnes det en egenskap for canvas kalt mozImageSmoothingEnabled (se), men den hjelper ikke i andre nettlesere. Og den fjerner også jevning av stier.
Kodeutdrag:
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
Jeg skrev veiledningen i din første lenke.
Bare for å rydde luften:
shapeCanvas.style.width = w + 'px';
shapeCanvas.style.height = h + 'px';
er egentlig ikke verdt å gjøre. Det er ingen vits i å sette stilen hvis det bare er et midlertidig lerret, og du bør egentlig aldri ønske å sette bredde- og høyde-stil på et lerret uansett, det forvirrer bare ting.
Det ellisbben sa i kommentaren er stort sett det som skjer.
Det er mulig å komme rundt det på noen få hackete måter, tipper jeg. En måte kan være å sørge for at det aldri tegnes på en heltallig piksel. En annen kan være å bruke ctx.scale(.99,.99) før man tegner noe, slik at det alltid blir anti-aliasing. Det er vanskelig å få en konsistent løsning her fordi forskjellige nettleseres implementeringer av anti-aliasing er forskjellige.
Her er noen eksperimenter fra meg selv:
http://jsfiddle.net/KYZYT/29/
De to første er formen tegnet fra et lerret og også tegnet fra en PNG
De to siste er det samme paret, men skalert med .99,.99
Den siste er den ekte. Den er fortsatt litt uskarp, men ser mye skarpere ut enn når man bruker bildene.
Ingen av eksperimentene mine fører til en slutt på din pulsering, i hvert fall ikke på mikroskopisk nivå. Jeg tror dette er noe du bare må leve med hvis du vil animere piksel-perfekte bilder på halv-piksel plasser.
Hvis du virkelig føler at du ikke bare kan tegne på perfekte piksler, er ditt (andre) beste alternativ for konsistens sannsynligvis å finne en måte å tvinge anti-aliasing på til enhver tid. Å sørge for at du alltid blir oversatt til et ikke-heltall eller skalere det en anelse er gode kandidater, men det kan finnes andre.
For å være ærlig, ditt beste alternativ er å ikke cache disse animerte banene før du absolutt trenger ytelsen fra dem. Cache de stiliserte knappene og andre statiske ting du lager, men hvis du har animerte baner som trenger å bevege seg sakte og presist og se veldig bra ut, kan du like gjerne holde deg til den ekte løsningen fremfor min caching-optimalisering med mindre du virkelig trenger det for dem også.
Svar #2
Litt skamløs reklame, men: Jeg har implementert jevn animasjon i HTML5-spillautomater på en litt hackete måte. Det genererte bufrede bildet blir tegnet på et lite lerret en gang, og så brukte jeg translate3d() med -moz-transform/-webkit-transform-stiler for lerretet for å flytte, speile og skalere bildet.
Forhåndsgenerering
- Opprett bilde
- Tegn bildeinnhold
- Opprett lerretsobjekt i DOM
Animasjonsfase
- Tøm lerret
- Tegn den bufrede bildet på lerretet
- Bruk CSS3-transformasjoner (scale3d og translate3d) for å flytte lerretet rundt.
