【问题标题】:Problem with saving canvas as image on server using toDataURL();使用 toDataURL() 将画布保存为服务器上的图像时出现问题;
【发布时间】:2020-03-22 00:08:07
【问题描述】:

关于这个问题的更详细背景是here,我试图查看该线程中链接的答案,这就是我卡住的地方。

A 已经创建了一个 PHP 文件,该文件通过另一个线程中显示的 ajax 调用在另一个 PHP 文件中的 div 元素内通过 ajax 加载。 PHP文件使用数据库拉取和带有chart.js的html来制作图表画布,我想将其保存为服务器上的图像。

下面是我正在尝试做的代码示例。

// html stuff to make a html canvas is above here.
// PHP stuff to take post data, get data from database and insert it into barChartData and chartOptions vars is above here. (these two vars contain all the chart.js config stuff and the chart works.)

var ctx = document.getElementById("Cloudbar1").getContext("2d");
Chart.defaults.global.defaultFontColor = 'black';
Chart.defaults.global.defaultFontFamily = "Arial, sans-serif";
Chart.defaults.global.defaultFontSize = 16;
window.myBar = new Chart(ctx, {
type: "bar",
data: barChartData,
options: chartOptions
});

//convert canvas element to a url format.
var dataURL = ctx.toDataURL();

//now send the file to the server with yet another ajax call.
$.ajax({
type: "POST",
url: "savecanvasimgtoserver.php",
data: { 
imgBase64: dataURL
}
})

尝试运行时,我收到控制台错误提示

"Uncaught TypeError: ctx.toDataURL is not a function"

我认为这并不重要,因为我还没有在上述错误的代码中得到这么多,但是上面代码指向的 php 文件只包含:

$img = $_POST['data'];
$img = str_replace('data:image/png;base64,', '', $img);
$img = str_replace(' ', '+', $img);
$fileData = base64_decode($img);
//saving
$fileName = 'chart.png';
file_put_contents($fileName, $fileData);

取自this问题。

据我所知,当你得到一个“x 不是函数”而它是一个函数时,这意味着你试图放入函数中的东西在某种程度上是无效的,但“ctx”无疑是画布元素并在这种情况下有效。

【问题讨论】:

  • 如果你把 ctx.toDataURL();进入数据 imgBase64 属性而不是变量。我怀疑图表还没有准备好(加载、创建)。
  • 它应该是画布而不是上下文不应该吗? developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/…
  • @cptnk 带着这种怀疑,我尝试在 8 秒超时内将 ajax 调用封装到 savecanvasimagetoserver.php 中。 (没有改变)然后做你的建议,但我以前从来没有这样做过,所以不确定语法,我尝试将它作为“数据:{ imgBase64:ctx.toDataURL()”} 但这给出了完全相同的错误. (我确实记得注释掉上面的转换行!)
  • 哦,我差点忘记了,有一件东西我应该扔进去,以防万一。所有这些的输出(我使图表显示在通过 jquery show() 弹出的隐藏 div 中)并且在运行此处讨论的代码时,该 div 仍然隐藏在 display:none; 的内联 css 中。 .这会阻止我使用此画布 todataURL() 功能吗?

标签: javascript php jquery html ajax


【解决方案1】:

只需使用 var ctx = document.getElementById("Cloudbar1");作为 ctx 它将起作用。您无需获取 2D 上下文即可从画布元素生成 dataurl,并确保脚本与图像的请求 url 位于同一域中以避免 CORS 错误。

【讨论】:

  • 假设我正确理解了您的建议,我尝试了data: { imgBase64: document.getElementById("Cloudbar1").toDataURL() } 这导致脚本运行没有错误,但给了我一个 0kb 无效的 png 图像。
  • 这取决于数据之前和之后的执行方式:{} 尝试在数据对象之外调用它,然后再添加变量引用,或者您甚至可以以毫秒为单位减慢执行速度。
  • 好的,这让我有目的地。使用上述方法,它有点工作。如果我将此包含在几秒钟的超时中,然后通过在超时触发之前打开此图表显示的隐藏 div 来触发“show()”jquery,则此代码可以创建图像。但是,如果我不打开这个 div 并让 8 秒超时命中,PHP 文件会在服务器上创建一个无法打开的无效 .png 文件。所以肯定是有问题的 div 是隐藏的事实导致了问题。我不想为我的用例向用户显示图表。
【解决方案2】:

感谢@cptnk 和@steveola,他们之间的合作让我找到了正确的答案。

在我的情况下,问题是由于 toDataURL() 似乎无法解析设置为 display:none; 的元素。因此,即使您运行必要的 Javascript 来进行计算,如果它没有在浏览器中呈现,toDataURL() 也无法捕获图像数据。

我的解决方案是更改我的显示/隐藏方法,从将元素从 css display:none 和 display:block 切换为使用视口外的位置,在我的情况下,我现在在 9999 的最高值之​​间切换和 0 在有问题的元素上。感觉有点像 hack,但它确实有效。

我想也可以使用诸如将可见性从 0 切换到 100 之类的方法(因为图像仍然被渲染,所以它只是变得透明),但在我的用例中,我不希望元素占用在页面中腾出空间并拦截其后面元素上的点击事件,它仍然会在 css 可见性下。

// This show/hide method stops toDataURL(); from capturing the canvas data if it's not visible at the time it's run.
$(document).on('click', '#showreport1link', function(){
                    $('#chartpreview1').show();
                });

// This show/hide method does not.
$(document).on('click', '#showreport1link', function(){
                    $('#chartpreview1').css('top', '0px'); //was top: 9999; until clicked.
                });

【讨论】:

    猜你喜欢
    • 2017-11-19
    • 2014-06-17
    • 2018-02-20
    • 2019-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多