2012-06-22 7 views
5

Ich begann gerade mit dem HTML5 Canvas zu spielen und ich hoffte, ein paar Spiele damit zu machen. Sobald jedoch begann ich machte die Maus zu koordiniert, geschliffen es zu einem fast zum Stillstand:Redrapping HTML5 Leinwand unglaublich langsam

http://jsfiddle.net/mnpenner/zHpgV/

Alles, was ich tat, war, machen 38 Linien und einen Text, soll es in der Lage sein, damit umzugehen, nicht wahr?

Mache ich etwas falsch? Ich würde gerne mindestens 30 FPS rendern können, aber für so etwas würde ich erwarten, dass es 1000 Mal malen kann.

Oder verwende ich gerade das falsche Werkzeug für den Job? Ist WebGL für die Aufgabe bereit? Warum sollte man so viel langsamer sein als der andere?

String.prototype.format = function() { 
 
    var args = arguments; 
 
    return this.replace(/\{(\d+)\}/g, function(m, n) { 
 
     return args[n]; 
 
    }); 
 
}; 
 
var $canvas = $('#canvas'); 
 
var c = $canvas[0].getContext('2d'); 
 
var scale = 20; 
 
var xMult = $canvas.width()/scale; 
 
var yMult = $canvas.height()/scale; 
 
var mouseX = 0; 
 
var mouseY = 0; 
 
c.scale(xMult, yMult); 
 
c.lineWidth = 1/scale; 
 
c.font = '1pt Calibri'; 
 

 
function render() { 
 
    c.fillStyle = '#dcb25c'; 
 
    c.fillRect(0, 0, scale, scale); 
 
    c.fillStyle = '#544423'; 
 
    c.lineCap = 'square'; 
 
    for (var i = 0; i <= 19; ++i) { 
 
     var j = 0.5 + i; 
 
     c.moveTo(j, 0.5); 
 
     c.lineTo(j, 19.5); 
 
     c.stroke(); 
 
     c.moveTo(0.5, j); 
 
     c.lineTo(19.5, j); 
 
     c.stroke(); 
 
    } 
 
    c.fillStyle = '#ffffff'; 
 
    c.fillText('{0}, {1}'.format(mouseX, mouseY), 0.5, 1.5); 
 
} 
 
render(); 
 
$canvas.mousemove(function(e) { 
 
    mouseX = e.clientX; 
 
    mouseY = e.clientY; 
 
    render(); 
 
});
<canvas id="canvas" width="570" height="570"></canvas>

Antwort

7

Hier ist der Code viel besser gemacht.

http://jsfiddle.net/zHpgV/3/

Hier ist eine Aufschlüsselung der Dinge, die Sie berücksichtigen sollten, die ich geändert:

  • Continuous anstatt anzuhalten und die Schaffung eines neuen Weg mit beginPath auf einen Pfad hinzufügen. Dies ist bei weitem der größte Performance-Killer hier. Sie enden mit einem Pfad mit Tausenden und Abertausenden von Zeilensegmenten, die niemals gelöscht werden.
  • Kontinuierlich den gleichen Pfad wieder und wieder machen, wenn es nur einmal bei der Initialisierung gemacht werden muss. Das heißt, das einzige, was Sie innerhalb von render anrufen müssen, ist stroke. Sie müssen lineTo/moveTo nie wieder anrufen, und natürlich nicht kontinuierlich. Siehe 1.
  • beachten zweimal Streicheln für einen Weg
  • innen ein Streicheln für Schleife
  • einen Hintergrund Redrawing statt Einstellung CSS Hintergrund
  • die Linie Kappe über Einstellung und über

Anmerkung 1 : Wenn Sie mehr als einen Pfad in Ihrer Anwendung haben möchten, sollten Sie wahrscheinlich Pfade wie diesen cachen, da sie sich nie ändern. Ich habe ein Tutorial, wie man das macht here.

Natürlich, Wenn Sie all dies tun, um nur einen Hintergrund zu machen, sollte es als PNG gespeichert werden und Sie sollten ein CSS-Hintergrundbild verwenden.

Wie so: http://jsfiddle.net/zHpgV/4/

Dann wird Ihr plötzlich Routine machen ist eher klein:

function render() { 
    c.clearRect(0, 0, scale, scale); 
    c.fillText('{0}, {1}'.format(mouseX, mouseY), 0.5, 1.5); 
} 
+0

Ich wusste nicht, dass Wege das bearbeitete! Ich denke, ein "Pfad" -Objekt wäre intuitiver gewesen. Sinn macht, warum es jetzt so langsam war, danke! – mpen

+3

Es gibt nun ein Pfadobjekt in der HTML5-Canvas-Spezifikation und Sie können einen Pfad erstellen und 'drawPath' in Zukunft aufrufen. Aber kein Browser hat es bereits implementiert und es kann Monate dauern, bis Sie es nutzen können. Kaufen Sie eines Tages! –

7

Sie müssen nicht das gesamte Netz in jedem Animationsrahmen zeichnen. Setzen Sie sie auf eine andere darunterliegende Leinwand (es ist üblich, sie als "Ebenen" zu bezeichnen, aber sie sind nur einzelne Elemente der Leinwand), so dass Sie nur Koordinaten neu zeichnen können.

<div id="canv"> 
<canvas id="bgLayer" width="500" height="500" style="z-index: 0"></canvas> 
<canvas id="fgLayer" width="500" height="500" style="z-index: 1"></canvas> 
</div> 

Hier ist the example Ich habe mit geschichteter Leinwand zu spielen. Die Tabelle auf der unteren Leinwand gezeichnet, sind Kugeln auf der oberen Leinwand gezeichnet. Es ist nur ein Spielplatz, es gibt also viel zu reparieren und zu optimieren, zum Beispiel um jeden Ball nur einmal auf einer anderen versteckten Leinwand zu zeichnen und getImageData/putImageData zu verwenden, um die Leistung zu verbessern.

Es wird auch empfohlen, requestAnimationFrame zu verwenden, um die Zeichenfläche zu aktualisieren. Ihr Beispiel zeichnet stattdessen jede Mausbewegung auf, dies ist viel häufiger erforderlich (wenn sich die Maus natürlich bewegt).

Es gibt eine gute article zur Verbesserung der Leinwand Leistung. Auch gibt es eine große SO post zu diesem Thema.

+0

eine Weile Nahm mich Sie buchstäblich um herauszufinden, dazu gedacht, die Canvas-Elemente Schicht. Ich dachte, dass "Schichten" ein Konzept innerhalb des Leinwandkontexts wären. Das ist eine gute Idee. Danke für die Tipps! – mpen

+0

Entschuldigung, ich habe es geändert, um zu vermeiden, dass jemand anderes verwirrt wird. –

+1

Das sind gute Kommentare, aber es gibt ein anderes Problem, das ich in diesem Fall nicht finde. Ich zeichne sehr viel komplexere Dinge mit sehr schnellen Animationen, ohne mich um Doppelpufferung zu kümmern. –

9

Wie ich in Kommentaren gesagt habe, war ich überrascht von der Langsamkeit dieses Codes, da ich viel komplexere Dinge mit sehr schnellen Animationen zeichne, ohne sich um Doppelpufferungen zu kümmern.

So sah ich ein wenig mehr und fand einen Fehler wie erwartet.

Das Hauptproblem ist die Anhäufung des Zeichenpfads.

Fügen Sie jedes Mal, wenn Sie einen Pfad zeichnen, c.beginPath(); hinzu.

Hier ist ein fast rendering of the same thing, um zu beweisen, dass es jetzt fliegt.

Leinwand Zeichnung ist schnell und kann für Animationen verwendet werden.