html5 - élément canvas - Plusieurs couches

176

Sans aucune bibliothèque d'extension, est-il possible d'avoir plusieurs calques dans le même élément de canevas?

Donc, si je fais un clearRect sur le calque supérieur, il n'effacera pas celui du bas?

Merci.

Grégoire
la source
vous pouvez jeter un oeil à radikalfx.com/2009/10/16/canvas-collage . il utilise une sorte de technique de "couches".
Matthew
2
vérifiez ceci .. html5.litten.com/using-multiple-html5-canvases-as-layers cela vous aidera à résoudre votre problème de manière appropriée
Dakshika
@Dakshika Merci pour ce lien, il expliquait un problème que j'avais eu lors de l'utilisation du canevas il y a quelques années qu'une bibliothèque a pris en charge pour moi.
Fering le

Réponses:

267

Non, cependant, vous pouvez <canvas>superposer plusieurs éléments les uns sur les autres et accomplir quelque chose de similaire.

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

Dessinez votre premier calque sur la layer1toile et le deuxième calque sur la layer2toile. Ensuite, lorsque vous êtes clearRectsur la couche supérieure, tout ce qui se trouve sur la toile inférieure apparaîtra.

Jimr
la source
existe-t-il un moyen de masquer / afficher un calque .. de sorte que je puisse masquer layer1 et afficher layer2 et faire l'inverse si nécessaire .. ??
Zaraki
4
Vous pouvez le cacher avec CSS - ie display: none;. Ou effacez simplement le canevas, s'il n'est pas très coûteux de le redessiner lorsque le calque doit être affiché.
jimr
Les valeurs attribuées à «gauche» et «haut» doivent être «0px» et non «0».
Bryan Green
6
@BryanGreen Pas vrai. "Cependant, pour les longueurs nulles, l'identificateur d'unité est facultatif (c'est-à-dire qu'il peut être représenté de manière syntaxique par le <number> 0)." w3.org/TR/css3-values/#lengths
xehpuk
Puis-je contrôler le type de composition pour plusieurs canevas?
ziyuang
40

Liés à ceci:

Si vous avez quelque chose sur votre canevas et que vous voulez dessiner quelque chose à l'arrière de celui-ci - vous pouvez le faire en modifiant le paramètre context.globalCompositeOperation sur 'destination-over' - puis le remettre à 'source-over' lorsque vous ' c'est fait.

   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
la source
oui, c'est bien mais en cas d'effacement, comme demandé en question. cela effacera les deux couches parallèlement. ce qui est encore une fois incorrect.
Pardeep Jain
27

Vous pouvez créer plusieurs canvaséléments sans les ajouter au document. Ce seront vos couches :

Ensuite, faites ce que vous voulez avec eux et à la fin, rendez simplement leur contenu dans le bon ordre sur le canevas de destination en utilisant drawImagesur context.

Exemple:

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

Et voici quelques codepen: https://codepen.io/anon/pen/mQWMMW

juszczak
la source
4
@SCLeo vous avez dit que "Performance killer. Environ ~ 10 fois plus lent" est complètement faux. Selon les cas d'utilisation, l'utilisation d'un seul canevas DOM et le rendu des toiles hors écran sont plus rapides que l'empilement de canevas dans DOM. L'erreur courante est l'analyse comparative des appels de rendu, les appels de dessin de canevas peuvent être chronométrés, le rendu DOM est en dehors du contexte Javascripts et ne peut pas être chronométré. Le résultat est que le canevas empilé be DOM n'obtient pas le rendu de composition (fait par DOM) inclus dans le benchmark.
Blindman67
@ Blindman67 Je sais ce que tu veux dire. Vérifiez simplement ce benchmark: jsfiddle.net/9a9L8k7k/1 . En cas de malentendu, il y a trois toiles, canvas 1 (ctx1) est une vraie toile. Le canevas 2 (ctx2) et le canevas 3 (ctx) sont hors écran. L'image a été préalablement rendue sur ctx3. Dans le test 1 de ce benchmark, je rend directement ctx3 sur ctx1. Dans le test 2, je rend ctx3 sur ctx2 puis ctx2 en ctx1. Le test 2 est 30 fois plus lent que le test 1 sur mon ordinateur. C'est pourquoi je dis que l'utilisation d'une toile intermédiaire est beaucoup plus lente.
SCLeo
@ Blindman67 L'astuce du canevas hors écran ne fonctionne que lorsque le canevas hors écran est statique. L'utilisation de toiles dynamiques endommagera considérablement les performances. (Encore une fois, ce que j'essaie de dire, c'est que la toile hors écran dynamique est extrêmement lente, donc vraisemblablement cette technique (simuler des couches par plusieurs canevas hors écran) ne sera pas souhaitable)
SCLeo
@ Blindman67 IMPORTANT: L'adresse du benchmark est https://jsfiddle.net/9a9L8k7k/3 , j'oublie de sauvegarder après l'édition et Stack Overflow ne me laisse plus changer le commentaire précédent ...
SCLeo
4
@ Blindman67 Je suis désolé, c'est mon erreur. J'ai testé et constaté que l'utilisation de plusieurs toiles hors écran se déroule très bien. Je ne sais toujours pas pourquoi cette référence montre que l'utilisation de la toile hors écran est si lente.
SCLeo
6

J'avais le même problème aussi, je me suis mis à plusieurs éléments de canevas avec position: absolu fait le travail, si vous voulez enregistrer la sortie dans une image, cela ne fonctionnera pas.

Je suis donc allé de l'avant et j'ai fait un simple "système" de couches pour coder comme si chaque couche avait son propre code, mais tout est rendu dans le même élément.

https://github.com/federicojacobi/layeredCanvas

J'ai l'intention d'ajouter des capacités supplémentaires, mais pour l'instant, cela fera l'affaire.

Vous pouvez faire plusieurs fonctions et les appeler afin de "faux" calques.

Federico Jacobi
la source
Celui-ci est parfait.
Nadir du
4

Vous pouvez également consulter http://www.concretejs.com qui est un framework de canevas Html5 moderne et léger qui permet la détection des coups, la superposition et beaucoup d'autres choses périphériques. Vous pouvez faire des choses comme ceci:

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
la source
De quelle manière ces couches finiront-elles dans le DOM? Chacun accessible via CSS?
Garavani
0

Je comprends que le Q ne souhaite pas utiliser de bibliothèque, mais je proposerai ceci à d'autres personnes issues de recherches Google. @EricRowell a mentionné un bon plugin, mais il y a aussi un autre plugin que vous pouvez essayer, html2canvas .

Dans notre cas, nous utilisons des PNG transparents en couches avec z-indexun widget de "création de produit". Html2canvas a travaillé avec brio pour faire bouillir la pile sans pousser les images, ni utiliser les complexités, les solutions de contournement et le canevas «non réactif» lui-même. Nous n'avons pas pu faire cela de manière fluide / saine avec la toile vanille + JS.

Première utilisation z-indexsur des div absolus pour générer du contenu en couches dans un wrapper positionné relatif. Ensuite, dirigez le wrapper à travers html2canvas pour obtenir un canevas rendu, que vous pouvez laisser tel quel, ou afficher sous forme d'image afin qu'un client puisse l'enregistrer.

Dhaupin
la source
Si vous avez des images plus lourdes, la conversion du HTML en canevas prendra un certain temps, nous avons dû nous en éloigner simplement parce que le rendu prenait beaucoup de temps.
Vilius le
@Vilius ouais bon appel sur les images lourdes / grandes. Nous avons essayé de nous en tenir à des images de 300K ou moins avec pas plus de 4 couches, sinon les clients en difficulté ressentiraient la brûlure lors du téléchargement de l'image compostée finale. Curieux, qu'avez-vous passé à ce temps réduit?
dhaupin
Eh bien, nous avons fait une grosse erreur en utilisant des éléments html pour dessiner quelque chose en premier lieu. Parce que notre API a renvoyé x, y, largeur et hauteur, nous sommes passés à jscanavs pour dessiner l'image au lieu d'utiliser des éléments html. Remarquez que nous avons eu quelques problèmes avec la rotation (les points de départ étaient un peu gênants et imprévisibles) et l'application d'images en utilisant des dimensions spécifiques, mais tout a finalement été résolu. Nous avons également constaté que notre application de traitement d'images épuisait beaucoup de ressources, nous nous sommes donc éloignés de cela également.
Vilius
0

mais la couche 02 couvrira tous les dessins de la couche 01. Je l'ai utilisé pour montrer le dessin dans les deux couches. utilisez (background-color: transparent;) avec style.

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