web-dev-qa-db-fra.com

HTML5 / Canvas prend-il en charge la double mise en mémoire tampon?

Ce que j'aimerais faire, c'est dessiner mes graphiques sur un tampon et ensuite pouvoir les copier tels quels sur le canevas afin de pouvoir faire des animations et éviter le scintillement. Mais je n'ai pas trouvé cette option. Quelqu'un sait comment je peux m'y prendre?

79
foreyez

Le lien utile suivant, en plus d'afficher des exemples et des avantages de l'utilisation de la double mise en mémoire tampon, montre plusieurs autres conseils de performance pour l'utilisation de l'élément canvas html5. Il comprend des liens vers les tests jsPerf, qui regroupent les résultats des tests entre les navigateurs dans une base de données Browserscope. Cela garantit que les conseils de performance sont vérifiés.

http://www.html5rocks.com/en/tutorials/canvas/performance/

Pour votre commodité, j'ai inclus un exemple minimal de double tampon efficace comme décrit dans l'article.

// canvas element in DOM
var canvas1 = document.getElementById('canvas1');
var context1 = canvas1.getContext('2d');

// buffer canvas
var canvas2 = document.createElement('canvas');
canvas2.width = 150;
canvas2.height = 150;
var context2 = canvas2.getContext('2d');

// create something on the canvas
context2.beginPath();
context2.moveTo(10,10);
context2.lineTo(10,30);
context2.stroke();

//render the buffered canvas onto the original canvas element
context1.drawImage(canvas2, 0, 0);
32
ricksuggs

Une méthode très simple consiste à avoir deux éléments de canevas au même emplacement d'écran et à définir la visibilité du tampon que vous devez afficher. Dessinez sur le caché et retournez lorsque vous avez terminé.

Du code:

CSS:

canvas { border: 2px solid #000; position:absolute; top:0;left:0; 
visibility: hidden; }

Retourner dans JS:

Buffers[1-DrawingBuffer].style.visibility='hidden';
Buffers[DrawingBuffer].style.visibility='visible';

DrawingBuffer=1-DrawingBuffer;

Dans ce code, le tableau 'Buffers []' contient les deux objets canvas. Donc, lorsque vous voulez commencer à dessiner, vous devez toujours obtenir le contexte:

var context = Buffers[DrawingBuffer].getContext('2d');
83
Fedor van Eldijk

Les navigateurs que j'ai testés gèrent tous cette mise en mémoire tampon pour vous en ne repeignant pas le canevas tant que le code qui dessine votre cadre n'est pas terminé. Voir également la liste de diffusion WHATWG: http://www.mail-archive.com/[email protected]/msg19969.html

19
Edward Coffey

Vous pouvez toujours faire var canvas2 = document.createElement("canvas"); et ne pas l'ajouter du tout au DOM.

Je dis juste que vous avez l'air obsédé par display:none; cela me semble plus propre et imite l'idée d'une double mise en mémoire tampon de manière plus précise que d'avoir simplement une toile maladroitement invisible.

13
DeadlyBacon

Plus de deux ans plus tard:

Il n'est pas nécessaire d'implémenter "manuellement" la double mise en mémoire tampon. M. Geary a écrit à ce sujet dans son livre "HTML5 Canvas" .

Pour réduire efficacement le scintillement, utilisez requestAnimationFrame() !

8
ohager

Pour les incroyants, voici un code clignotant. Notez que j'efface explicitement pour effacer le cercle précédent.

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

function draw_ball(ball) {
    ctx.clearRect(0, 0, 400, 400);
    ctx.fillStyle = "#FF0000";
    ctx.beginPath();
    ctx.arc(ball.x, ball.y, 30, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fill();
}

var deltat = 1;
var ball = {};
ball.y = 0;
ball.x = 200;
ball.vy = 0;
ball.vx = 10;
ball.ay = 9.8;
ball.ax = 0.1;

function compute_position() {
    if (ball.y > 370 && ball.vy > 0) {
        ball.vy = -ball.vy * 84 / 86;
    }
    if (ball.x < 30) {
        ball.vx = -ball.vx;
        ball.ax = -ball.ax;
    } else if (ball.x > 370) {
        ball.vx = -ball.vx;
        ball.ax = -ball.ax;
    }
    ball.ax = ball.ax / 2;
    ball.vx = ball.vx * 185 / 186;
    ball.y = ball.y + ball.vy * deltat + ball.ay * deltat * deltat / 2
    ball.x = ball.x + ball.vx * deltat + ball.ax * deltat * deltat / 2
    ball.vy = ball.vy + ball.ay * deltat
    ball.vx = ball.vx + ball.ax * deltat
    draw_ball(ball);
}

setInterval(compute_position, 40);
<!DOCTYPE html>
<html>
<head><title>Basketball</title></head>
<body>
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
</body></html>
6
John

Josh a demandé (il y a quelque temps) comment le navigateur sait "quand le processus de dessin se termine" afin d'éviter le scintillement. J'aurais commenté directement son post mais mon représentant n'est pas assez haut. C'est aussi juste mon opinion. Je n'ai pas de faits pour le confirmer, mais je me sens assez confiant à ce sujet et il pourrait être utile à d'autres de le lire à l'avenir.

Je suppose que le navigateur ne "sait" pas quand vous avez fini de dessiner. Mais tout comme la plupart des javascript, tant que votre code s'exécute sans abandonner le contrôle au navigateur, le navigateur est essentiellement verrouillé et ne pourra/ne pourra pas mettre à jour/répondre à son interface utilisateur. Je suppose que si vous effacez le canevas et dessinez l'intégralité de votre cadre sans abandonner le contrôle au navigateur, il ne dessinera pas votre canevas tant que vous n'aurez pas terminé.

Si vous configurez une situation où votre rendu s'étend sur plusieurs appels setTimeout/setInterval/requestAnimationFrame, où vous effacez le canevas en un seul appel et dessinez des éléments sur votre canevas au cours des prochains appels, en répétant le cycle (par exemple) tous les 5 appels, je Je serais prêt à parier que vous verriez un scintillement puisque la toile serait mise à jour après chaque appel.

Cela dit, je ne suis pas sûr d'avoir confiance en cela. Nous sommes déjà au point que javascript est compilé en code machine natif avant exécution (du moins c'est ce que fait le moteur V8 de Chrome d'après ce que je comprends). Je ne serais pas surpris si cela ne prenait pas trop de temps avant que les navigateurs commencent à exécuter leur javascript dans un thread distinct de l'interface utilisateur et à synchroniser tout accès aux éléments de l'interface utilisateur permettant à l'interface utilisateur de se mettre à jour/répondre pendant l'exécution de javascript qui n'accédait pas à l'interface utilisateur. Quand/si cela se produit (et je comprends qu'il y a beaucoup d'obstacles qui devraient être surmontés, tels que les gestionnaires d'événements qui démarrent pendant que vous utilisez encore un autre code), nous verrons probablement un scintillement sur l'animation de toile qui n'utilise pas une sorte de double tampon.

Personnellement, j'adore l'idée de deux éléments de toile positionnés les uns sur les autres et alternés qui sont affichés/dessinés sur chaque cadre. Assez discret et probablement assez facilement ajouté à une application existante avec quelques lignes de code.

6
Lee

Il n'y a pas de scintillement dans les navigateurs Web! Ils utilisent déjà la mise en mémoire tampon dbl pour leur rendu. Le moteur Js fera tout votre rendu avant de le montrer. En outre, le contexte enregistre et restaure uniquement les données matricielles transformationnelles de la pile et autres, pas le contenu du canevas lui-même. Donc, vous n'avez pas besoin ou ne voulez pas de tampon dbl!

5
Luka

Plutôt que de rouler le vôtre, vous obtiendrez probablement le meilleur kilométrage en utilisant une bibliothèque existante pour créer une animation JavaScript propre et sans scintillement:

En voici un populaire: http://processingjs.org

4
a7drew

vous avez besoin de 2 canevas: (remarquez le z-index css et la position: absolue)

<canvas id="layer1" width="760" height="600" style=" position:absolute; top:0;left:0; 
visibility: visible;  z-index: 0; solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
<canvas id="layer2" width="760" height="600" style="position:absolute; top:0;left:0; 
visibility: visible;  z-index: 1; solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>

vous pouvez remarquer que le premier canevas est visible et le second il est caché l'idée c'est de dessiner sur le caché après cela nous cacherons le visible et rendrons le canevas caché visible. quand il est caché

<script type="text/javascript">
var buff=new Array(2);
buff[0]=document.getElementById("layer1");
buff[1]=document.getElementById("layer2");

ctx[0]=buff[0].getContext("2d");
ctx[1]=buff[1].getContext("2d");
var current=0;
// draw the canvas (ctx[ current ]);
buff[1- current ].style.visibility='hidden';
buff[ current ].style.visibility='visible';
ctx[1-current].clearRect(0,0,760,600);
current =1-current;
3
Aviad Gispan

Opera 9.10 est très lent et montre le processus de dessin. Si vous voulez qu'un navigateur n'utilise pas la double mise en mémoire tampon, essayez Opera 9.10 out.

Certaines personnes ont suggéré que les navigateurs déterminent d'une manière ou d'une autre la fin du processus de dessin, mais pouvez-vous expliquer comment cela peut fonctionner? Je n'ai pas remarqué de scintillement évident dans Firefox, Chrome ou IE9 même lorsque le dessin est lent, il semble donc que c'est ce qu'ils font, mais comment cela est un mystère pour moi. Comment le navigateur pourrait-il savoir qu'il rafraîchit l'affichage juste avant que d'autres instructions de dessin soient exécutées? Pensez-vous qu'ils viennent juste de le chronométrer, donc si un intervalle de plus de 5 ms se passe sans exécuter une instruction de dessin sur toile, cela suppose qu'il peut échanger des tampons en toute sécurité?

2
Josh

Dans la plupart des situations, vous n'avez pas besoin de le faire, le navigateur l'implémente pour vous. Mais pas toujours utile!

Vous devez toujours l'implémenter lorsque votre dessin est très compliqué. La plupart du taux de mise à jour de l'écran est d'environ 60 Hz, cela signifie que les mises à jour de l'écran par 16 ms. Le taux de mise à jour du navigateur peut être proche de ce nombre. Si votre forme a besoin de 100 ms pour être terminée, vous verrez une forme inachevée. Vous pouvez donc implémenter la double mise en mémoire tampon dans cette situation.

J'ai fait un test: Clear a rect, wait for some time, then fill with some color. Si je règle le temps sur 10 ms, je ne verrai pas de scintillement. Mais si je la mets à 20 ms, le scintillement se produit.

2
Chien-Wei Huang