html5 - element canvas - wiele warstw

176

Czy bez żadnej biblioteki rozszerzeń można mieć wiele warstw w tym samym elemencie Canvas?

Więc jeśli zrobię clearRect na górnej warstwie, to nie usunie dolnej?

Dzięki.

Gregoire
źródło
możesz zajrzeć na radikalfx.com/2009/10/16/canvas-collage . używa techniki „warstw”.
Matthew,
2
sprawdź to .. html5.litten.com/using-multiple-html5-canvases-as-layers pomoże ci to rozwiązać problem we właściwy sposób
Dakshika,
@Dakshika Dziękuję za ten link, który wyjaśnił problem, który miałem z płótnem kilka lat temu, a którym zajęła się za mnie biblioteka.
Fering

Odpowiedzi:

267

Nie, jednak możesz nałożyć wiele <canvas>elementów jeden na drugi i osiągnąć coś podobnego.

<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>

Narysuj pierwszą warstwę na layer1płótnie, a drugą na layer2płótnie. Następnie, gdy jesteś clearRectna górnej warstwie, prześwituje wszystko, co znajduje się na niższym płótnie.

jimr
źródło
czy istnieje sposób na ukrycie / pokazanie warstwy .. tak, że mogę ukryć warstwę 1 i pokazać warstwę 2 i zrobić odwrotnie, gdy jest to wymagane .. ??
Zaraki
4
Można to ukryć za pomocą CSS - tj display: none;. Lub po prostu wyczyść płótno, jeśli ponowne narysowanie go ponownie, gdy warstwa powinna zostać wyświetlona, ​​nie jest zbyt kosztowne.
Jimr
Wartości przypisane do „left” i „top” muszą mieć wartość „0px”, a nie „0”.
Bryan Green
6
@BryanGreen Nie prawda. „Jednak dla zerowych długości identyfikator jednostki jest opcjonalny (tj. Może być składniowo reprezentowany jako <numer> 0)”. w3.org/TR/css3-values/#lengths
xehpuk
Czy mogę kontrolować typ kompozycji dla wielu kanw?
ziyuang
40

Powiązane z tym:

Jeśli masz coś na płótnie i chcesz narysować coś z tyłu - możesz to zrobić, zmieniając ustawienie context.globalCompositeOperation na „destination-over” - a następnie przywróć je do „source-over”, kiedy to zrobisz gotowe.

   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
źródło
tak, to jest w porządku, ale w przypadku skasowania, jak zadano w pytaniu. spowoduje to równoległe wymazanie obu warstw. co znowu nie jest poprawne.
Pardeep Jain
27

Możesz tworzyć wiele canvaselementów bez dołączania ich do dokumentu. To będą twoje warstwy :

Następnie rób z nimi, co chcesz, a na koniec po prostu renderuj ich zawartość w odpowiedniej kolejności na docelowym płótnie, używając drawImageon context.

Przykład:

/* 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);

A oto kilka kodów: https://codepen.io/anon/pen/mQWMMW

juszczak
źródło
4
@SCLeo powiedziałeś, że „zabójca wydajności. Około ~ 10 razy wolniej” jest całkowicie błędny. W zależności od przypadków użycia użycie pojedynczego płótna DOM i renderowanie do niego kanw poza ekranem jest szybsze niż układanie kanw w DOM. Częstym błędem jest testowanie wywołań renderowania, wywołania rysowania kanwy mogą być synchronizowane w czasie, renderowanie DOM jest poza kontekstem JavaScript i nie można ich ustawić w czasie. W rezultacie płótno ułożone w stos be DOM nie otrzymuje renderowania kompozycji (wykonanego przez DOM) zawartego w
teście
@ Blindman67 Wiem, co masz na myśli. Po prostu sprawdź ten test porównawczy: jsfiddle.net/9a9L8k7k/1 . Jeśli źle zrozumiesz, są trzy płótna, płótno 1 (ctx1) to prawdziwe płótno. Canvas 2 (ctx2) i canvas 3 (ctx) są poza ekranem. Obraz został wcześniej wyrenderowany na ctx3. W teście 1 tego testu porównawczego renderuję bezpośrednio ctx3 na ctx1. W teście 2 renderuję ctx3 na ctx2, a następnie ctx2 na ctx1. Test 2 jest 30 razy wolniejszy niż test 1 na moim komputerze. Dlatego mówię, że używanie pośredniego płótna jest znacznie wolniejsze.
SCLeo
@ Blindman67 Sztuczka z płótnem poza ekranem działa tylko wtedy, gdy płótno poza ekranem jest statyczne. Używanie dynamicznych płócien znacznie obniży wydajność. (Ponownie, próbuję powiedzieć, że dynamiczne płótno poza ekranem jest wyjątkowo wolne, więc przypuszczalnie ta technika (pozorowanie warstw przez wiele
obszarów
@ Blindman67 WAŻNE: Adres benchmarku to https://jsfiddle.net/9a9L8k7k/3 , zapomniałem zapisać po edycji, a przepełnienie stosu nie pozwala mi już zmienić poprzedniego komentarza ...
SCLeo
4
@ Blindman67 Przepraszam, to mój błąd. Przetestowałem i stwierdziłem, że korzystanie z wielu kanw poza ekranem działa bardzo płynnie. Nadal nie jestem pewien, dlaczego ten test porównawczy pokazuje, że używanie płótna poza ekranem jest tak wolne.
SCLeo
6

Miałem też ten sam problem, podczas gdy wiele elementów płótna z pozycją: absolutne spełnia swoje zadanie, jeśli chcesz zapisać wynik do obrazu, to nie zadziała.

Więc poszedłem do przodu i zrobiłem prosty "system" warstwowania, aby zakodować tak, jakby każda warstwa miała swój własny kod, ale wszystko jest renderowane w tym samym elemencie.

https://github.com/federicojacobi/layeredCanvas

Zamierzam dodać dodatkowe możliwości, ale na razie wystarczy.

Możesz wykonywać wiele funkcji i wywoływać je w celu „sfałszowania” warstw.

Federico Jacobi
źródło
Ten jest doskonały.
Nadir
4

Możesz również sprawdzić http://www.concretejs.com, który jest nowoczesnym, lekkim frameworkiem HTML5, który umożliwia wykrywanie trafień, nakładanie warstw i wiele innych peryferyjnych rzeczy. Możesz robić takie rzeczy:

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
źródło
W jaki sposób te warstwy trafią do DOM? Każdy dostępny przez CSS?
Garavani
0

Rozumiem, że Q nie chce korzystać z biblioteki, ale zaoferuję to innym osobom pochodzącym z wyszukiwań Google. @EricRowell wspomniał o dobrej wtyczce, ale jest też inna wtyczka, którą możesz wypróbować, html2canvas .

W naszym przypadku używamy warstwowych przezroczystych plików PNG z z-indexwidżetem „konstruktora produktu”. Html2canvas działał znakomicie, aby ugotować stos bez przesuwania obrazów ani stosowania zawiłości, obejść i samego „niereagującego” płótna. Nie byliśmy w stanie zrobić tego płynnie / rozsądnie z płótnem waniliowym + JS.

Pierwsze użycie z-indexna absolutnych elementach DIV w celu wygenerowania zawartości warstwowej we względnie pozycjonowanym opakowaniu. Następnie przepuść opakowanie przez html2canvas, aby uzyskać renderowane płótno, które możesz pozostawić bez zmian, lub wyprowadzić jako obraz, aby klient mógł go zapisać.

dhaupin
źródło
Jeśli masz cięższe obrazy, konwersja HTML do płótna zajmie trochę czasu, musieliśmy odejść od tego tylko dlatego, że renderowanie trwało długo.
Vilius
@Vilius yeah dobre wezwanie na ciężkie / duże obrazy. Próbowaliśmy trzymać się 300 KB lub mniej obrazów z nie więcej niż 4 warstwami, w przeciwnym razie klienci, którzy mają problemy z zasobami, poczuliby pieczenie podczas pobierania ostatecznego skompostowanego obrazu. Ciekawe, co przeszedłeś do tego skróconego czasu?
dhaupin,
Cóż, popełniliśmy duży błąd, używając elementów HTML do narysowania czegoś w pierwszej kolejności. Ponieważ nasz interfejs API zwrócił x, y, szerokość i wysokość, przeszliśmy do jscanavs, aby narysować obraz zamiast używać elementów html. Pamiętaj, że mieliśmy kilka problemów z obracaniem (punkty początkowe były nieco niezręczne i nieprzewidywalne) oraz nakładaniem na nie obrazów przy użyciu określonych wymiarów, ale wszystko zostało ostatecznie rozwiązane. Odkryliśmy również, że nasza aplikacja do przetwarzania obrazów pochłaniała dużo zasobów, więc również odeszliśmy od tego.
Vilius,
0

ale warstwa 02 pokryje wszystkie rysunki w warstwie 01. Użyłem tego do pokazania rysunków w obu warstwach. użyj stylu (background-color: transparent;).

    <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
źródło