javascript - HTML5 Canvas: Elementi nacrtani s/cached za drawImage su izglajani kada se skaliraju i/ili pomiču
Znam za slučaj vrijednosti s pomičnim/zapreminskim brojevima za drawImage-ove x i y. Ali ono što mi treba je glatka animacija s mogućnošću spremanja mojih oblika u predmemoriju.
- Članak o predmemoriranju složenih putanja s rezervnim platnom
- Članak o
drawImages parametrima tipa float
Na primjer, želim nacrtati neki složeni oblik (npr. SVG-tigra, pretvoren u canvas-naredbe) na canvas samo jednom, a zatim ga glatko pomaknuti pomoću ctx.translate i ctx.drawImage. Trebam tada decimalne vrijednosti, jer inače dobivam pomicanje korak po korak:
Evo primjera na JSFiddle:
- Jedan: Brza brzina, s primjenom
Math.floornatranslateparametre (xiysu jednaki vremenu u sekundama pomnoženom s10): Animacija je čudna (sekvencijalna, nije glatka). - Dva: Spora brzina, s primjenom
Math.floornatranslateparametre (xiysu jednaki vremenu u sekundama): Animacija je čudna (sekvencijalna, nije glatka). - Tri: Brza brzina, bez zaokruživanja, vrijednosti s pomičnim zarezom (
xiysu jednaki vremenu u sekundama pomnoženom s10). Brzina je brza, pa animacija izgleda dobro. - Četiri: Spora brzina, bez zaokruživanja, decimalne vrijednosti (
xiysu jednaki vremenu u sekundama). Brzina je spora, pa animacija izgleda pulsirajuće. Zašto?
Posljednji slučaj je onaj koji me zbunjuje. Griješim li u svojim pokušajima i postoji li mogućnost da ovaj trik keširanja dobro funkcionira?
U Firefoxu postoji svojstvo canvasa nazvano mozImageSmoothingEnabled (vidi), ali to ne pomaže u drugim preglednicima. Također uklanja zamućenje putanja.
Izdvojeni kod:
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) {
. . .
}
Odgovor #1
Napisao sam vodič u tvojoj prvoj poveznici.
Samo da razjasnimo situaciju:
shapeCanvas.style.width = w + 'px';
shapeCanvas.style.height = h + 'px';
nije stvarno vrijedno raditi. Nema smisla postavljati stil ako je to samo platno u memoriji, i zapravo nikada ne biste trebali htjeti postaviti stil širine i visine platna, to samo zakomplicira stvari.
Ono što je ellisbben rekao u komentaru je uglavnom ono što se događa.
Moguće je zaobići to na nekoliko hakerskih načina, siguran sam. Jedan način bi mogao biti da se pazi da se nikada ne crta na cijeli piksel. Drugi način bi mogao biti korištenje ctx.scale(.99,.99) prije crtanja bilo čega da bi uvijek bilo anti-aliasirano. Teško je dobiti konzistentno rješenje ovdje jer implementacije anti-aliasinga različitih preglednika variraju.
Evo nekoliko eksperimenata od mene:
http://jsfiddle.net/KYZYT/29/
Prva dva su oblik nacrtan s platna i također nacrtan iz PNG-a
Druga dva su isti par, ali skalirani s .99,.99
Posljednji je prava stvar. Još uvijek malo zamagljuje, ali izgleda puno oštrije nego kada se koriste slike.
Nijedan od mojih eksperimenata ne vodi kraju tvog pulsiranja, barem ne na mikroskopskoj razini. Mislim da je to nešto s čime ćeš se morati pomiriti ako želiš animirati slike savršene po pikselu na polu-pikselnim prostorima.
Ako zaista osjetite da ne možete jednostavno crtati na savršenim pikselima, onda je vaš (drugi) najbolji izbor za konzistentnost vjerojatno pronaći način da uvijek prisilite anti-aliasing. Osiguravanje da ste uvijek prevedeni na ne-cjelobrojnu vrijednost ili da ga malo skalirate su pristojne opcije, ali mogu postojati i druge.
Iskreno, najbolje je da ne keširate ove animirane putanje dok apsolutno ne trebate njihovu izvedbu. Keširajte stilizirane gumbe i druge statične stvari koje napravite, ali ako imate animirane putanje koje trebaju polako i precizno kretati i izgledati jako dobro, bolje je držati se pravog rješenja umjesto moje optimizacije keširanja, osim ako vam zaista to ne treba i za njih.
Odgovor #2
Bit ću bezobrazan, ali: Implementirao sam glatku animaciju u HTML5 slot igri na pomalo hakirani način. Generirana predmemorirana slika je nacrtana na malom platnu jednom, a zatim sam koristio translate3d() s -moz-transform/-webkit-transform stilovima za platno kako bih pomicao, preslikao i skalirao sliku.
Predgeneracija
- Stvori sliku
- Nacrtaj sadržaj slike
- Stvori canvas objekt u DOM-u
Faza animacije
- Očisti platno
- Nacrtaj predmemoriranu sliku na platno
- Koristite CSS3 transformacije (scale3d i translate3d) za pomicanje platna.
