2012-09-21 7 views
8

estoy tratando de convertir el elemento canvas en this page a un png usando el siguiente fragmento (por ejemplo, entrar en la consola de JavaScript):¿Por qué mi lienzo se queda en blanco después de convertirlo en imagen?

(function convertCanvasToImage(canvas) { 
    var image = new Image(); 
    image.src = canvas.toDataURL("image/png"); 
    return image; 
})($$('canvas')[0]); 

Por desgracia, el png que consigo es completamente en blanco. Observe también que el lienzo original queda en blanco después de cambiar el tamaño de la página.

¿Por qué el canvas queda en blanco? ¿Cómo puedo convertir este canvas a png?

+0

¿Usted consigue los errores? ¿Es '$$' un error tipográfico, o tiene jQuery usando un signo de dólar doble, en lugar de solo? – MrOBrian

+1

'$$' es una función incorporada a la consola JavaScript de Chrome, que se comporta de manera similar a jQuery. No obtengo ningún error, no. – Randomblue

+0

Ah, no sabía eso ... Estoy pensando que está pasando algo con esa página. Si hago 'toDataURL()' en el lienzo de esa página, obtengo una imagen en blanco de 500x500 y el lienzo se borra. Si hago exactamente el mismo código en el sitio en el que estoy trabajando que utiliza lienzo, obtengo los datos correctos y el lienzo no se borra. – MrOBrian

Respuesta

14

Kevin Reid's preserveDrawingBuffer sugerencia es la correcta, pero hay (generalmente) una mejor opción. El tl; dr es el código al final.

Puede ser costoso juntar los píxeles finales de una página web renderizada, y coordinar eso con la renderización del contenido WebGL aún más. El flujo habitual es:

  1. JavaScript cuestiones Comandos de dibujo con el contexto WebGL
  2. retornos de JavaScript, devolver el control al bucle principal evento navegador
  3. contexto WebGL convierte dibujo búfer (o su contenido) hacia el compositor de integración en la página web actualmente está representando en la pantalla
  4. página, con contenido de WebGL, que se muestra en la pantalla

Tenga en cuenta que esto es diferente de la mayoría de las aplicaciones OpenGL. En ellos, el contenido renderizado generalmente se muestra directamente, en lugar de componerse con muchas otras cosas en una página, algunas de las cuales pueden combinarse con el contenido WebGL.

La especificación WebGL se cambió para tratar el búfer de dibujo como esencialmente vacío después del paso 3. El código que está ejecutando en devtools viene después del paso 4, por lo que obtiene un búfer vacío. Este cambio en la especificación permitió grandes mejoras de rendimiento en las plataformas donde el borrado después del Paso 3 es básicamente lo que realmente sucede en el hardware (como en muchas GPU móviles). Si desea evitar esto para que algunas veces haga copias del contenido WebGL después del paso 3, el navegador debería siempre hacer una copia del búfer de dibujo antes del paso 3, lo que hará que su velocidad de fotogramas caiga precipitadamente en algunas plataformas.

Puede hacer exactamente eso y forzar al navegador a realizar la copia y mantener el contenido de la imagen accesible configurando preserveDrawingBuffer en verdadero. Desde la especificación:

Este comportamiento predeterminado se puede cambiar estableciendo el atributo preserveDrawingBuffer del objeto WebGLContextAttributes. Si este indicador es verdadero, el contenido del buffer de dibujo se conservará hasta que el autor lo borre o sobrescriba.Si este indicador es falso, intentar realizar operaciones usando este contexto como una imagen de origen después de que la función de renderización haya retornado puede conducir a un comportamiento indefinido. Esto incluye llamadas readPixels o toDataURL, o usar este contexto como la imagen fuente de la llamada texImage2D o drawImage de otro contexto.

En el ejemplo que nos ha facilitado, el código es simplemente cambiando la línea de creación del contexto:

gl = canvas.getContext("experimental-webgl", {preserveDrawingBuffer: true}); 

sólo tener en cuenta que va a obligar a que la ruta más lenta en algunos navegadores y el rendimiento se verá afectada, dependiendo de qué y cómo estás renderizando Debería estar bien en la mayoría de los navegadores de escritorio, donde la copia no tiene que estar hecha en realidad, y esos constituyen la gran mayoría de los navegadores con capacidad WebGL ... pero solo por ahora.

Sin embargo,, hay otra opción (como se menciona de manera algo confusa en el siguiente párrafo de la especificación).

Esencialmente, usted mismo hace la copia antes del paso 2: después de que todas sus llamadas al sorteo hayan terminado pero antes de que regrese al control del navegador desde su código. Esto es cuando el búfer de dibujo WebGL todavía está en contacto y es accesible, y entonces no debería tener problemas para acceder a los píxeles. Utiliza las mismas llamadas toDataUrl o readPixels que usaría, de lo contrario, es el momento lo que es importante.

Aquí obtienes lo mejor de ambos mundos. Obtiene una copia del búfer de dibujo, pero no lo paga en cada fotograma, incluso en aquellos en los que no necesita una copia (que puede ser la mayoría), como lo hace con preserveDrawingBuffer establecido en verdadero.

En el ejemplo que nos ha facilitado, sólo tiene que añadir su código a la parte inferior de drawScene y debería ver la copia de la tela justo debajo:

function drawScene() { 
    ... 

    var webglImage = (function convertCanvasToImage(canvas) { 
    var image = new Image(); 
    image.src = canvas.toDataURL('image/png'); 
    return image; 
    })(document.querySelectorAll('canvas')[0]); 

    window.document.body.appendChild(webglImage); 
} 
+1

Gracias. Esto explica por qué la imagen png estaba en blanco, pero no explica por qué la * imagen original * queda en blanco. – Randomblue

+1

fue solo la parte {preserveDrawingBuffer: true} para mí, gran respuesta aunque – FrickeFresh

2

Aquí hay algunas cosas que probar. No sé si cualquiera de estos debería ser necesario para que esto funcione, pero podrían marcar la diferencia.

  • Agregue preserveDrawingBuffer: true a los atributos getContext.
  • Intente hacer esto con un tutorial posterior que hace animación; es decir, se dibuja en el lienzo varias veces en lugar de solo una vez.
Cuestiones relacionadas