通过 canvas.toDataURL 将 canvas 保存到图像会产生黑色矩形
Saving canvas to image via canvas.toDataURL results in black rectangle
我正在使用 Pixi.js 并尝试将动画帧保存到图像中。 canvas.toDataUrl 应该可以,但我得到的只是一个黑色矩形。查看实例 here
我用来提取图像数据和设置图像的代码是:
var canvas = $('canvas')[0];
var context = canvas.getContext('2d');
$('button').click(function() {
var data = renderer.view.toDataURL("image/png", 1);
//tried var data = canvas.toDataURL();
$('img').attr('src', data);
})
[注意]
虽然这个答案是公认的,但请务必阅读下面@gman 的 ,它确实包含一种更好的方法。
你的问题是你使用的是webGL context,那么你需要将webGL context的preserveDrawingBuffer
属性设置为true
才能调用toDataURL()
方法。
或者,您可以强制 pixi 使用 2D 上下文,方法是使用 CanvasRenderer
Class
我知道在 SO 上至少已经回答了 5 次,但是...
Kaiido 提到的会起作用,但真正的问题是 canvas,当与 WebGL 一起使用时,默认情况下有 2 个缓冲区。您正在绘制的缓冲区和正在显示的缓冲区。
当您开始在 WebGL canvas 中绘图时,只要您退出当前事件,例如您的 requestAnimationFrame 回调,canvas 就会被标记为交换这 2 个缓冲区。当浏览器重新绘制页面时,它会进行交换。您正在绘制的缓冲区与正在显示的缓冲区交换。您现在正在绘制到其他缓冲区。该缓冲区已清除。
它被清除而不是单独留下的原因是浏览器是否实际交换缓冲区或做其他事情取决于浏览器。例如,如果抗锯齿打开(这是默认设置),那么它实际上不会进行交换。它做了 "resolve"。它将您刚刚绘制的高分辨率缓冲区转换为正常的分辨率抗锯齿副本并放入显示缓冲区。
因此,为了使其更加一致,无论浏览器采用哪种默认方式,它总是会清除您要绘制到的任何缓冲区。否则你不知道它有 1 帧旧数据还是 2 帧旧数据。
设置preserveDrawingBuffer: true
告诉浏览器"always copy, never swap"。在这种情况下,它不必清除绘图缓冲区,因为绘图缓冲区中的内容始终是已知的。没有交换。
这一切有什么意义?关键是,如果你想调用 toDataURL
或 gl.readPixels
,你需要在同一事件中调用它。
例如,您的代码可以像这样工作
var capture = false;
$('button').click(function() {
capture = true;
});
function render() {
renderer.render(...);
if (capture) {
capture = false;
var data = renderer.view.toDataURL("image/png", 1);
$('img').attr('src', data);
}
requestAnimationFrame(render);
}
requestAnimationFrame(render);
在那种情况下,因为您在呈现给它的同一 javascript 事件中调用 toDataURL
,无论是否有枯萎,您都会得到正确的结果 preserveDrawingBuffer
是否为真或错误。
如果您正在编写不经常呈现的应用程序,您也可以执行类似
的操作
$('button').click(function() {
// render right now
renderer.render(...);
// capture immediately
var data = renderer.view.toDataURL("image/png", 1);
$('img').attr('src', data);
});
默认情况下 preserveDrawingBuffer
为 false 的原因是因为交换比复制更快,所以这允许浏览器尽可能快地运行。
另见 this answer for one other detail
我正在使用 Pixi.js 并尝试将动画帧保存到图像中。 canvas.toDataUrl 应该可以,但我得到的只是一个黑色矩形。查看实例 here
我用来提取图像数据和设置图像的代码是:
var canvas = $('canvas')[0];
var context = canvas.getContext('2d');
$('button').click(function() {
var data = renderer.view.toDataURL("image/png", 1);
//tried var data = canvas.toDataURL();
$('img').attr('src', data);
})
[注意]
虽然这个答案是公认的,但请务必阅读下面@gman 的
你的问题是你使用的是webGL context,那么你需要将webGL context的preserveDrawingBuffer
属性设置为true
才能调用toDataURL()
方法。
或者,您可以强制 pixi 使用 2D 上下文,方法是使用 CanvasRenderer
Class
我知道在 SO 上至少已经回答了 5 次,但是...
Kaiido 提到的会起作用,但真正的问题是 canvas,当与 WebGL 一起使用时,默认情况下有 2 个缓冲区。您正在绘制的缓冲区和正在显示的缓冲区。
当您开始在 WebGL canvas 中绘图时,只要您退出当前事件,例如您的 requestAnimationFrame 回调,canvas 就会被标记为交换这 2 个缓冲区。当浏览器重新绘制页面时,它会进行交换。您正在绘制的缓冲区与正在显示的缓冲区交换。您现在正在绘制到其他缓冲区。该缓冲区已清除。
它被清除而不是单独留下的原因是浏览器是否实际交换缓冲区或做其他事情取决于浏览器。例如,如果抗锯齿打开(这是默认设置),那么它实际上不会进行交换。它做了 "resolve"。它将您刚刚绘制的高分辨率缓冲区转换为正常的分辨率抗锯齿副本并放入显示缓冲区。
因此,为了使其更加一致,无论浏览器采用哪种默认方式,它总是会清除您要绘制到的任何缓冲区。否则你不知道它有 1 帧旧数据还是 2 帧旧数据。
设置preserveDrawingBuffer: true
告诉浏览器"always copy, never swap"。在这种情况下,它不必清除绘图缓冲区,因为绘图缓冲区中的内容始终是已知的。没有交换。
这一切有什么意义?关键是,如果你想调用 toDataURL
或 gl.readPixels
,你需要在同一事件中调用它。
例如,您的代码可以像这样工作
var capture = false;
$('button').click(function() {
capture = true;
});
function render() {
renderer.render(...);
if (capture) {
capture = false;
var data = renderer.view.toDataURL("image/png", 1);
$('img').attr('src', data);
}
requestAnimationFrame(render);
}
requestAnimationFrame(render);
在那种情况下,因为您在呈现给它的同一 javascript 事件中调用 toDataURL
,无论是否有枯萎,您都会得到正确的结果 preserveDrawingBuffer
是否为真或错误。
如果您正在编写不经常呈现的应用程序,您也可以执行类似
的操作$('button').click(function() {
// render right now
renderer.render(...);
// capture immediately
var data = renderer.view.toDataURL("image/png", 1);
$('img').attr('src', data);
});
默认情况下 preserveDrawingBuffer
为 false 的原因是因为交换比复制更快,所以这允许浏览器尽可能快地运行。
另见 this answer for one other detail