2017-06-26 3 views
2

J'ai un problème inattendu avec Chrome (actuel). J'ai une application web qui utilise le canvas webGL pour faire des manipulations d'image de base et qui plus tard copie le canvas webGl dans un canvas 2d afin de sauvegarder les données de l'image. Le problème est que le contexte de dessin de la toile webgl semble être perdu dans Chrome après avoir changé d'onglet. Cela ne se produit pas dans les autres navigateurs que j'ai essayés (FF, IE 11/Edge, Safari).Comment empêcher Chrome de disposer (?) De mon contexte de dessin Webgl après le changement d'onglet?

Je m'attendrais à ce que Chrome déclenche également l'événement de perte de contexte webgl si c'est bien ce qui se passe, mais je n'obtiens jamais cet événement.

Voici un violon très simple qui démontre le problème.

https://jsfiddle.net/JoeBlow/m14evgzj/

1) Charg qui tire sur une toile WebGL

2) Passer à un nouvel onglet

3) Revenez à l'onglet d'origine

4) Cliquez sur « Copier Image'. Si je réinitialise le canevas 3d (recharger tout et redessiner), tout fonctionne comme prévu même après un changement d'onglet, mais j'essayais d'éviter de capturer les événements de flou/focus sur la fenêtre/fenêtre d'affichage.

est ici le code simplifié:

window.canvasSource = document.getElementById("source"); 
var canvasDestination = document.getElementById("destination"); 
var buttonCopy = document.getElementById("btnCopy"); 
var btnToggle = document.getElementById("btnToggle"); 
var btnClear = document.getElementById("btnClear"); 
var btnInit3d = document.getElementById('btnInit3d'); 

var canvasOptions = { preserveDrawingBuffer: true }; 

//var sourceContext = canvasSource.getContext("2d", options); 
var destinationContext = canvasDestination.getContext("2d", canvasOptions); 

window.canvasSource.addEventListener("webglcontextlost", function(event) { 
console.log('context lost'); 
    event.preventDefault(); 
}, false); 

window.canvasSource.addEventListener("webglcontextrestored", init3d, false); 

btnToggle.addEventListener('click', function() { 
    if (window.canvasSource.style.visibility != "hidden") { 
     window.canvasSource.style['visibility']='hidden'; 
    } 
    else { 
    window.canvasSource.style['visibility'] = 'visible'; 
    } 
}); 

btnCopy.addEventListener('click', function() { 
    destinationContext.drawImage(window.canvasSource,0,0); 
}); 

btnClear.addEventListener('click', function() { 
    destinationContext.clearRect(0,0, 1500, 1500); 
}); 

btnInit3d.addEventListener('click', function() { 
    init3d(); 
}); 

function init3d() { 
console.log('init3d()'); 
window.gl = window.canvasSource.getContext("webgl", canvasOptions); 
var gl = window.gl; 
if (gl==null) 
console.log("couldn't get webgl context"); 
else 
console.log(gl); 

gl.viewport(0, 0, window.canvasSource.width, window.canvasSource.height); 
gl.clearColor(0, 0.5, 0, 1); 
gl.clear(gl.COLOR_BUFFER_BIT); 

var v = document.getElementById("vertex").firstChild.nodeValue; 
var f = document.getElementById("fragment").firstChild.nodeValue; 

var vs = gl.createShader(gl.VERTEX_SHADER); 
gl.shaderSource(vs, v); 
gl.compileShader(vs); 

var fs = gl.createShader(gl.FRAGMENT_SHADER); 
gl.shaderSource(fs, f); 
gl.compileShader(fs); 

program = gl.createProgram(); 
gl.attachShader(program, vs); 
gl.attachShader(program, fs); 
gl.linkProgram(program); 
} 

init3d(); 
+0

https://bugs.chromium.org/p/chromium/issues/detail?id=639105 Pour contourner ce problème ... Je ne suis pas sûr, mais en tirant le contexte WebGL sur un 2d on pourrait faire ([une Q/A connexe sur l'enregistrement d'un flux webgl] (https://stackoverflow.com/questions/44156528/canvas-doesnt-repaint-when-tab-inactive-backgrounded-for-recording-webgl/44172801 # 44172801)) – Kaiido

+0

Je n'ai pas remarqué de solution de contournement, sauf si vous faites référence à l'utilisation de setTimeout plutôt que requestAnimationFrame (qui ne dessine toujours pas lorsque l'onglet n'est pas mis au point). Ce problème est probablement lié à ce que je vis, mais le principal symptôme est qu'une fois que vous éteignez puis revenez à un onglet avec une toile de rendu webgl, l'image affichée dans le canevas sera toujours correcte, mais jusqu'à ce que vous dessinez une nouvelle frame (après réinitialisation complète, vous ne pouvez pas l'utiliser comme source pour une opération de copie car le drawingBuffer sous-jacent qui faciliterait l'opération de copie a disparu – SDShooter

+0

Il n'y a pas de setTimeout dans le Q/A lié (sauf pour arrêter l'enregistrement, mais ce n'est vraiment que pour la démo et pas du tout nécessaire.) Le but de la réponse est d'utiliser une boucle de synchronisation faite maison basée sur WebAudioAPI, qui n'est pas liée au focus de l'onglet.En outre, il utilise un 2Dcontext pour l'enregistrement, qui peut également être une solution de contournement pour votre opération de copie. – Kaiido

Répondre

2

Je pense que ce problème est lié à this one.

Apparemment, le chrome efface le dessinBuffer lorsque la languette est floue, même si vous lui avez dit de ne pas le faire.

Une solution de contournement consiste à créer vous-même un objet preserveDrawingBuffer. 2DContext n'est pas affecté par ce bogue, donc au lieu de définir preserveDrawingBuffer lors de votre initiation wegbl, vous pouvez créer un contexte 2D hors écran, sur lequel vous allez dessiner le contexte webgl à chaque nouveau draw.

var canvasSource = document.getElementById("source"); 
 
var canvasDestination = document.getElementById("destination"); 
 
var buttonCopy = document.getElementById("btnCopy"); 
 
//var btnToggle = document.getElementById("btnToggle"); 
 
var btnClear = document.getElementById("btnClear"); 
 
var btnInit3d = document.getElementById('btnInit3d'); 
 
var gl; 
 

 
// we don't need to preserve the drawing buffer anymore 
 
// since we'll do it manually 
 
//var canvasOptions = { preserveDrawingBuffer: true}; 
 
var bufferContext = canvasSource.cloneNode().getContext('2d'); 
 
// 2D context don't have an preserveDrawingBuffer option 
 
var destinationContext = canvasDestination.getContext("2d"); 
 

 
btnCopy.addEventListener('click', function() { 
 
    var error = gl.getError(); 
 
    if (error) { 
 
    console.log('Gl Error: ' + error); 
 
    } 
 
    // we draw the buffer canvas instead of the webgl one 
 
    destinationContext.drawImage(bufferContext.canvas, 0, 0); 
 
}); 
 

 
btnClear.addEventListener('click', function() { 
 
    destinationContext.clearRect(0, 0, 1500, 1500); 
 
}); 
 

 
function init3d() { 
 
    gl = canvasSource.getContext("webgl"); 
 
    if (gl == null) 
 
    console.log("couldn't get webgl context"); 
 

 
    gl.viewport(0, 0, canvasSource.width, canvasSource.height); 
 
    gl.clearColor(0, 0.5, 0, 1); 
 
    gl.clear(gl.COLOR_BUFFER_BIT); 
 
    // at each draw, save the webgl state 
 
    bufferContext.drawImage(canvasSource, 0,0); 
 
} 
 

 
init3d();
<canvas id="source"> </canvas> 
 
<canvas id="destination"> </canvas> 
 
<div> 
 
    <button id="btnCopy">Copy Image</button> 
 
    <button id="btnClear">Clear Copy</button> 
 
</div>

+0

Merci, c'est une solution de contournement assez solide. – SDShooter