html5 - Canvas-Element - Mehrere Ebenen

176

Ist es ohne Erweiterungsbibliothek möglich, mehrere Ebenen im selben Canvas-Element zu haben?

Wenn ich also ein clearRect auf der obersten Ebene mache, wird die unterste nicht gelöscht?

Vielen Dank.

Gregoire
quelle
Vielleicht möchten Sie einen Blick auf radikalfx.com/2009/10/16/canvas-collage werfen . Er verwendet eine Art "Schichten" -Technik.
Matthew
2
Schauen Sie sich das an .. html5.litten.com/using-multiple-html5-canvases-as-layers Dies wird Ihnen helfen, Ihr Problem auf die richtige Weise zu lösen
Dakshika
@ Dakshika Vielen Dank für diesen Link, der ein Problem erklärt, das ich vor einigen Jahren bei der Verwendung der Leinwand hatte und das eine Bibliothek für mich erledigt hat.
Fering

Antworten:

267

Nein, Sie können jedoch mehrere <canvas>Elemente übereinander legen und etwas Ähnliches erreichen.

<div style="position: relative;">
 <canvas id="layer1" width="100" height="100" 
   style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
 <canvas id="layer2" width="100" height="100" 
   style="position: absolute; left: 0; top: 0; z-index: 1;"></canvas>
</div>

Zeichnen Sie Ihre erste Ebene auf die layer1Leinwand und die zweite Ebene auf die layer2Leinwand. Wenn Sie sich dann clearRectauf der obersten Ebene befinden, wird alles auf der unteren Leinwand durchscheinen.

jimr
quelle
Gibt es eine Möglichkeit, eine Ebene ein- oder auszublenden? So kann ich Ebene1 ausblenden und Ebene2 anzeigen und bei Bedarf umgekehrt. ??
Zaraki
4
Sie könnten es mit CSS verstecken - dh display: none;. Oder löschen Sie einfach die Leinwand, wenn es nicht sehr teuer ist, sie erneut zu zeichnen, wenn die Ebene angezeigt werden soll.
Jimr
Die Werte für 'left' und 'top' müssen '0px' und nicht '0' sein.
Bryan Green
6
@BryanGreen Nicht wahr. "Für Nulllängen ist die Einheitenkennung jedoch optional (dh kann syntaktisch als <Nummer> 0 dargestellt werden)." w3.org/TR/css3-values/#lengths
xehpuk
Kann ich den Kompositionstyp für mehrere Canvas steuern?
Ziyuang
40

Im Zusammenhang damit:

Wenn Sie etwas auf Ihrer Leinwand haben und etwas dahinter zeichnen möchten, können Sie dies tun, indem Sie die Einstellung context.globalCompositeOperation auf "Ziel-Over" ändern - und es dann auf "Quelle-Over" zurücksetzen, wenn Sie fertig.

   var context = document.getElementById('cvs').getContext('2d');

    // Draw a red square
    context.fillStyle = 'red';
    context.fillRect(50,50,100,100);



    // Change the globalCompositeOperation to destination-over so that anything
    // that is drawn on to the canvas from this point on is drawn at the back
    // of what's already on the canvas
    context.globalCompositeOperation = 'destination-over';



    // Draw a big yellow rectangle
    context.fillStyle = 'yellow';
    context.fillRect(0,0,600,250);


    // Now return the globalCompositeOperation to source-over and draw a
    // blue rectangle
    context.globalCompositeOperation = 'source-over';

    // Draw a blue rectangle
    context.fillStyle = 'blue';
    context.fillRect(75,75,100,100);
<canvas id="cvs" />

Richard
quelle
Ja, das ist in Ordnung, aber im Falle eines Löschens, wie in Frage gestellt. Dadurch werden beide Ebenen parallel gelöscht. das ist wieder nicht richtig.
Pardeep Jain
27

Sie können mehrere canvasElemente erstellen , ohne sie an ein Dokument anzuhängen. Dies werden Ihre Schichten sein :

Machen Sie dann mit ihnen, was Sie wollen, und rendern Sie am Ende ihren Inhalt in der richtigen Reihenfolge auf der Zielleinwand mit drawImageon context.

Beispiel:

/* using canvas from DOM */
var domCanvas = document.getElementById('some-canvas');
var domContext = domCanvas.getContext('2d');
domContext.fillRect(50,50,150,50);

/* virtual canvase 1 - not appended to the DOM */
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'blue';
ctx.fillRect(50,50,150,150);

/* virtual canvase 2 - not appended to the DOM */    
var canvas2 = document.createElement('canvas')
var ctx2 = canvas2.getContext('2d');
ctx2.fillStyle = 'yellow';
ctx2.fillRect(50,50,100,50)

/* render virtual canvases on DOM canvas */
domContext.drawImage(canvas, 0, 0, 200, 200);
domContext.drawImage(canvas2, 0, 0, 200, 200);

Und hier ist ein Codepen: https://codepen.io/anon/pen/mQWMMW

juszczak
quelle
4
@SCLeo du hast gesagt "Performance Killer. Ungefähr 10 mal langsamer" ist völlig falsch. Abhängig von den Anwendungsfällen ist die Verwendung einer einzelnen DOM-Zeichenfläche und das Rendern von Offscreen-Leinwänden schneller als das Stapeln der Zeichenfläche in DOM. Der häufigste Fehler ist das Benchmarking von Rendering-Aufrufen. Canvas-Draw-Aufrufe können zeitgesteuert werden. Das DOM-Rendering befindet sich außerhalb des Javascripts-Kontexts und kann nicht zeitgesteuert werden. Das Ergebnis ist, dass die be DOM-gestapelte Leinwand nicht das im Benchmark enthaltene Compositing-Rendering (von DOM) erhält.
Blindman67
@ Blindman67 Ich weiß was du meinst. Schauen Sie sich einfach diesen Benchmark an: jsfiddle.net/9a9L8k7k/1 . Falls Sie falsch verstehen, gibt es drei Leinwände, Leinwand 1 (ctx1) ist eine echte Leinwand. Canvas 2 (ctx2) und Canvas 3 (ctx) befinden sich außerhalb des Bildschirms. Das Bild wurde zuvor auf ctx3 gerendert. In Test 1 dieses Benchmarks rendere ich ctx3 direkt auf ctx1. In Test 2 rendere ich ctx3 auf ctx2 und dann ctx2 auf ctx1. Test 2 ist 30 Mal langsamer als Test 1 auf meinem Computer. Deshalb sage ich, dass die Verwendung einer Zwischenleinwand viel langsamer ist.
SCLeo
@ Blindman67 Der Trick der Off-Screen-Leinwand funktioniert nur, wenn die Off-Screen-Leinwand statisch ist. Die Verwendung dynamischer Leinwände beeinträchtigt die Leistung erheblich. (Wiederum versuche ich zu sagen, dass die dynamische Off-Screen-Leinwand extrem langsam ist, so dass diese Technik (Schein-Ebenen durch mehrere Off-Screen-Leinwand) vermutlich nicht wünschenswert ist.)
SCLeo
@ Blindman67 WICHTIG: Die Adresse des Benchmarks lautet https://jsfiddle.net/9a9L8k7k/3 . Ich habe vergessen, nach der Bearbeitung zu speichern, und Stack Overflow lässt mich den vorherigen Kommentar nicht mehr ändern ...
SCLeo
4
@ Blindman67 Es tut mir leid, es ist mein Fehler. Ich habe getestet und festgestellt, dass die Verwendung mehrerer Off-Screen-Canvas sehr reibungslos funktioniert. Ich bin mir immer noch nicht sicher, warum dieser Benchmark zeigt, dass die Verwendung von Leinwand außerhalb des Bildschirms so langsam ist.
SCLeo
6

Ich hatte auch das gleiche Problem, ich habe zwar mehrere Canvas-Elemente mit Position: Absolut erledigt den Job, wenn Sie die Ausgabe in einem Bild speichern möchten, wird das nicht funktionieren.

Also habe ich ein einfaches "System" für Ebenen erstellt, um zu codieren, als ob jede Ebene ihren eigenen Code hätte, aber alles wird in dasselbe Element gerendert.

https://github.com/federicojacobi/layeredCanvas

Ich beabsichtige, zusätzliche Funktionen hinzuzufügen, aber im Moment wird es reichen.

Sie können mehrere Funktionen ausführen und sie aufrufen, um Ebenen zu "fälschen".

Federico Jacobi
quelle
Dieser ist perfekt.
Nadir
4

Sie können auch http://www.concretejs.com überprüfen, ein modernes, leichtes HTML5-Canvas-Framework, das Treffererkennung, Layering und viele andere periphere Funktionen ermöglicht. Sie können solche Dinge tun:

var wrapper = new Concrete.Wrapper({
  width: 500,
  height: 300,
  container: el
});

var layer1 = new Concrete.Layer();
var layer2 = new Concrete.Layer();

wrapper.add(layer1).add(layer2);

// draw stuff
layer1.sceneCanvas.context.fillStyle = 'red';
layer1.sceneCanvas.context.fillRect(0, 0, 100, 100);

// reorder layers
layer1.moveUp();

// destroy a layer
layer1.destroy();
Eric Rowell
quelle
Auf welche Weise landen diese Ebenen im DOM? Jeder über CSS zugänglich?
Garavani
0

Ich verstehe, dass der Q keine Bibliothek verwenden möchte, aber ich werde dies für andere anbieten, die aus der Google-Suche stammen. @EricRowell erwähnte ein gutes Plugin, aber es gibt auch ein anderes Plugin, das Sie ausprobieren können, html2canvas .

In unserem Fall verwenden wir geschichtete transparente PNGs mit z-indexals "Product Builder" -Widget. Html2canvas hat hervorragend funktioniert, um den Stapel zu reduzieren, ohne Bilder zu verschieben oder Komplexitäten, Problemumgehungen und die "nicht reagierende" Leinwand selbst zu verwenden. Wir konnten dies mit der Vanille-Leinwand + JS nicht reibungslos / vernünftig machen.

Verwenden Sie diese z-indexOption zuerst für absolute Divs, um Ebeneninhalte in einem relativ positionierten Wrapper zu generieren. Führen Sie dann den Wrapper durch html2canvas, um eine gerenderte Zeichenfläche zu erhalten, die Sie unverändert lassen oder als Bild ausgeben können, damit ein Client sie speichern kann.

Dhaupin
quelle
Wenn Sie schwerere Bilder haben, dauert es einige Zeit, bis HTML in Canvas konvertiert ist. Wir mussten uns davon entfernen, nur weil das Rendern lange gedauert hat.
Vilius
@Vilius ja guter Anruf bei den schweren / großen Bildern. Wir haben versucht, bei 300K oder weniger Bildern mit nicht mehr als 4 Ebenen zu bleiben, da sonst ressourcenintensive Clients beim Herunterladen des endgültigen kompostierten Bildes das Brennen spüren würden. Neugierig, was haben Sie zu dieser reduzierten Zeit bewegt?
Dhaupin
Nun, wir haben einen großen Fehler gemacht, indem wir HTML-Elemente verwendet haben, um etwas zu zeichnen. Da unsere API x, y, width und height zurückgegeben hat, sind wir zu jscanavs gewechselt, um das Bild zu zeichnen, anstatt HTML-Elemente zu verwenden. Wohlgemerkt, wir hatten einige Probleme mit der Rotation (Startpunkte waren etwas umständlich und unvorhersehbar) und dem Anwenden von Bildern mit bestimmten Abmessungen, aber alles wurde schließlich gelöst. Wir haben auch festgestellt, dass unsere Bildverarbeitungsanwendung viel Ressourcen verbraucht, also haben wir uns auch davon entfernt.
Vilius
0

Aber Ebene 02 deckt alle Zeichnungen in Ebene 01 ab. Ich habe dies verwendet, um die Zeichnung in beiden Ebenen zu zeigen. Verwenden Sie (Hintergrundfarbe: transparent;) im Stil.

    <div style="position: relative;"> 
      <canvas id="lay01" width="500" height="500" style="position: absolute; left: 0; top: 0; z-index: 0; background-color: transparent;">
      </canvas> 
      <canvas id="lay02" width="500" height="500" style="position: absolute; left: 0; top: 0; z-index: 1; background-color: transparent;">
      </canvas>
</div>

Aymhenry
quelle