javascript - HTML5 Canvas: Elemente, die mit/cached für drawImage gezeichnet werden, werden geglättet, wenn sie skaliert und/oder verschoben werden
Ich weiß über den Fall von Fließkomma-/Ganzzahlwerten für drawImage's x und y Bescheid. Aber was ich brauche, ist eine flüssige Animation mit der Möglichkeit, meine Formen zu cachen.
- Artikel über das Zwischenspeichern komplexer Pfade mit Backup-Leinwand
- Artikel über
drawImagemit Fließkomma-Parametern
Zum Beispiel möchte ich eine komplexe Form (z. B. SVG-Tiger, in Canvas-Befehle konvertiert) einmal auf das Canvas zeichnen und sie dann fließend mit ctx.translate und ctx.drawImage bewegen. Dafür benötige ich dann die Float-Werte, denn sonst erhalte ich eine schrittweise Bewegung:
Hier sind die Beispiele bei JSFiddle:
- Eins: Hohe Geschwindigkeit, mit
Math.floorangewendet auftranslate-Parameter (xundysind gleich der Zeit in Sekunden multipliziert mit10): Die Animation ist seltsam (nacheinander, nicht flüssig). - Zwei: Langsame Geschwindigkeit, mit
Math.floorangewendet auftranslate-Parameter (xundyentsprechen der Zeit in Sekunden): Die Animation ist seltsam (sequenziell, nicht flüssig). - Drei: Hohe Geschwindigkeit, keine Rundung, Gleitkommawerte (
xundysind gleich der Zeit in Sekunden multipliziert mit10). Die Geschwindigkeit ist hoch, daher sieht die Animation gut aus. - Vier: Langsame Geschwindigkeit, keine Abrundung, Fließkommawerte (
xundyentsprechen der Zeit in Sekunden). Die Geschwindigkeit ist langsam, daher sieht die Animation pulsierend aus. Warum?
Der letzte Fall ist derjenige, der mich verwirrt. Liege ich mit meinen Versuchen falsch und gibt es eine Möglichkeit, diesen Caching-Trick gut funktionieren zu lassen?
In Firefox gibt es eine Eigenschaft von Canvas namens mozImageSmoothingEnabled (siehe), aber das hilft in anderen Browsern nicht. Und es entfernt auch die Pfadglättung.
Codeauszug:
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) {
. . .
}
Antwort #1
Ich habe das Tutorial in deinem ersten Link geschrieben.
Nur um die Luft zu klären:
shapeCanvas.style.width = w + 'px';
shapeCanvas.style.height = h + 'px';
ist wirklich nicht wert, gemacht zu werden. Es macht keinen Sinn, den Stil festzulegen, wenn es nur ein In-Memory-Canvas ist, und man sollte wirklich niemals die Breite und Höhe des Stils eines Canvas festlegen wollen, es verwirrt nur die Dinge.
Was ellisbben im Kommentar gesagt hat, ist ziemlich genau das, was passiert.
Es ist möglich, es auf ein paar hackige Weisen zu umgehen, denke ich. Eine Möglichkeit könnte sein, sicherzustellen, dass es niemals auf einem ganzen Pixel gezeichnet wird. Eine andere könnte sein, ctx.scale(.99,.99) zu verwenden, bevor etwas gezeichnet wird, damit es immer anti-aliasiert ist. Es ist schwierig, hier eine konsistente Lösung zu finden, weil die Anti-Aliasing-Implementierungen verschiedener Browser unterschiedlich sind.
Hier sind ein paar Experimente von mir:
http://jsfiddle.net/KYZYT/29/
Die ersten beiden sind die Form, die von einer Leinwand gezeichnet wurde, und auch die von einer PNG gezeichnete.
Die zweiten beiden sind dasselbe Paar, aber skaliert um .99,.99
Der letzte ist das echte Ding. Es ist immer noch ein bisschen unscharf, sieht aber viel schärfer aus, als wenn man die Bilder benutzt.
Keines meiner Experimente führt dazu, dass dein Pulsieren aufhört, zumindest nicht auf mikroskopischer Ebene. Ich denke, das ist einfach etwas, womit du leben musst, wenn du pixelgenaue Bilder auf halbe Pixelräume animieren willst.
Wenn du wirklich das Gefühl hast, dass du nicht einfach auf perfekten Pixeln zeichnen kannst, ist dein (zweiter) bester Ansatz für Konsistenz wahrscheinlich, einen Weg zu finden, Anti-Aliasing jederzeit zu erzwingen. Sicherzustellen, dass du immer auf eine nicht-ganzzahlige Position verschoben wirst oder es minimal skalierst, sind anständige Möglichkeiten, aber es könnte auch andere geben.
Um ehrlich zu sein, ist es am besten, diese animierten Pfade nicht zu cachen, bis Sie die Leistung davon wirklich benötigen. Cache die stilisierten Buttons und andere statische Dinge, die Sie erstellen, aber wenn Sie animierte Pfade haben, die sich langsam und präzise bewegen müssen und sehr gut aussehen sollen, können Sie genauso gut beim Original bleiben, anstatt meiner Caching-Optimierung zu folgen, es sei denn, Sie benötigen sie wirklich auch dafür.
Antwort #2
Ein bisschen schamloses Eigenlob, aber: Ich habe eine sanfte Animation in einem HTML5-Slotspiel auf eine etwas hackige Weise implementiert. Das erzeugte zwischengespeicherte Bild wird einmal auf ein kleines Canvas gezeichnet und dann habe ich translate3d() mit -moz-transform/-webkit-transform-Stilen für das Canvas verwendet, um das Bild zu bewegen, zu spiegeln und zu skalieren.
Vorgenerierung
- Bild erstellen
- Bildinhalt zeichnen
- Erstelle ein Canvas-Objekt im DOM
Animationsphase
- Leinwand löschen
- Zeichne das zwischengespeicherte Bild auf die Leinwand
- Verwenden Sie CSS3-Transformationen (scale3d und translate3d), um die Leinwand zu verschieben.
