Describe it simply:
A WebGL canvas rendered with preserveDrawingBuffer: false becomes transparent when captured with SnapDOM v2.22.0.
The same canvas is captured correctly with v2.18.0. The issue affects both PNG and SVG exports, while surrounding HTML elements remain visible.
Quick demo to reproduce
Save this as an HTML file and open it in a browser:
<!doctype html>
<html lang="en">
<body>
<div id="target">
<canvas id="canvas" width="640" height="320"></canvas>
<p>This text remains visible in the exported image.</p>
</div>
<button id="capture-png">Capture PNG</button>
<button id="capture-svg">Capture SVG</button>
<div id="result"></div>
<script type="module">
import { snapdom } from
'https://unpkg.com/@zumer/snapdom@2.22.0/dist/snapdom.mjs';
const canvas = document.querySelector('#canvas');
const gl = canvas.getContext('webgl2', {
alpha: true,
antialias: true,
preserveDrawingBuffer: false,
});
function render(time) {
const width = gl.drawingBufferWidth;
const height = gl.drawingBufferHeight;
const rectangleWidth = Math.round(width * 0.3);
const x = Math.round(
((Math.sin(time / 700) + 1) / 2) * (width - rectangleWidth)
);
gl.enable(gl.SCISSOR_TEST);
gl.scissor(0, 0, width, height);
gl.clearColor(0.03, 0.07, 0.14, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.scissor(x, 70, rectangleWidth, 180);
gl.clearColor(0.05, 0.8, 0.92, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
const target = document.querySelector('#target');
const result = document.querySelector('#result');
document.querySelector('#capture-png').addEventListener('click', async () => {
result.replaceChildren(
await snapdom.toPng(target, { backgroundColor: '#ffffff' })
);
});
document.querySelector('#capture-svg').addEventListener('click', async () => {
result.replaceChildren(
await snapdom.toSvg(target, { backgroundColor: '#ffffff' })
);
});
</script>
</body>
</html>
With v2.22.0, the exported canvas is transparent, although it is visible on the live page.
Changing the imported version to v2.18.0 restores the WebGL content in both PNG and SVG captures.
Anything else we should know?
The regression appears related to commit a5536ae, which made the canvas pre-requestAnimationFrame wait Safari-only.
For a WebGL context using preserveDrawingBuffer: false, toDataURL() can return a valid but fully transparent PNG after the frame has been presented. Because the data URL is neither empty nor equal to data:,, the existing retry is not triggered.
Waiting for an animation frame in a beforeClone plugin restores both exports:
const waitForAnimationFramePlugin = {
name: 'wait-for-animation-frame-before-clone',
beforeClone: () =>
new Promise((resolve) => {
requestAnimationFrame(() => resolve());
}),
};
const image = await snapdom.toPng(target, {
plugins: [waitForAnimationFramePlugin],
});
Bug reproduced with Google Chrome.
Same behavior with v2.23.2.
Describe it simply:
A WebGL canvas rendered with
preserveDrawingBuffer: falsebecomes transparent when captured with SnapDOM v2.22.0.The same canvas is captured correctly with v2.18.0. The issue affects both PNG and SVG exports, while surrounding HTML elements remain visible.
Quick demo to reproduce
Save this as an HTML file and open it in a browser:
With v2.22.0, the exported canvas is transparent, although it is visible on the live page.
Changing the imported version to v2.18.0 restores the WebGL content in both PNG and SVG captures.
Anything else we should know?
The regression appears related to commit a5536ae, which made the canvas pre-
requestAnimationFramewait Safari-only.For a WebGL context using
preserveDrawingBuffer: false,toDataURL()can return a valid but fully transparent PNG after the frame has been presented. Because the data URL is neither empty nor equal todata:,, the existing retry is not triggered.Waiting for an animation frame in a
beforeCloneplugin restores both exports:Bug reproduced with Google Chrome.
Same behavior with v2.23.2.