canvas 将 vega 与 nodejs 结合使用时出现问题(仅限服务器端)
Problem with canvas using vega with nodejs (server side only)
我已经在 Discord 机器人上工作了几个星期,它基本上是在服务器上编译统计数据并推断模式。为了改进它,我想让它生成 PNG 格式的图形,以便将它们发送回用户 - 简而言之,没有 DOM。
为了实现这一点,我目前正在使用 vega(5.10.1 版 - 最新版)和 node-canvas(2.6.1 版 - 最新版),以及 nodejs v12.16.1.
我一直在网上搜索有关 vega 使用的帮助,发现了一些相互矛盾的来源。我一直在使用此处提供的示例代码:
https://vega.github.io/vega/usage/
问题是我不断收到此错误:
TypeError: Cannot read property 'getContext' of null
message:"Cannot read property 'getContext' of null"
stack:"TypeError: Cannot read property 'getContext' of null
at resize (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-scenegraph\build\vega-scenegraph.js:3665:28)
at CanvasRenderer.prototype.resize (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-scenegraph\build\vega-scenegraph.js:3714:5)
at CanvasRenderer.prototype.initialize (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-scenegraph\build\vega-scenegraph.js:3294:17)
at CanvasRenderer.prototype.initialize (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-scenegraph\build\vega-scenegraph.js:3709:28)
at initializeRenderer (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-view\build\vega-view.js:657:8)
at renderHeadless (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-view\build\vega-view.js:780:12)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async View.renderToCanvas [as toCanvas] (e:\DEV\GIT REPOS\GITHUB\P...
这是给我带来麻烦的代码:
// Imports
const vega = require('vega');
// Render image from given graph spec (statsObject)
async function graphToImage (statsObject) {
graphObject = new vega.View(vega.parse(statsObject), { renderer: 'none'});
const pngName = generateFileName(10);
removeExistingFile(pngName);
graphObject.toCanvas().then(canvas => {
console.log('Writing PNG to file...');
writeFile(`../../../../generated/${pngName}.png`, canvas.toBuffer());
}).catch(err => {
console.log("Error writing PNG to file:");
console.error(err);
});
return pngName;
}
我真的不知道 canvas 或 vega 是如何工作的,所以我不知道是什么导致了这个问题以及如何解决它...但是,问题似乎出在里面toCanvas()
方法。非常感谢任何帮助!
提前致谢!
编辑:我设法解决了我的问题,我在这里发布答案以供将来通知:
我通过使用 view.toSVG()
而不是错误的 view.toCanvas()
将 View 对象直接渲染为 SVG 字符串,成功地生成了一张图表图片,效果很好。
然后,剩下要做的就是将得到的SVG字符串转换成PNG文件,就这样了。
这是更新后的工作代码:
// Imports
const vega = require('vega');
// Render image from given graph object
async function graphToImage (statsObject) {
// Generate a new 10-char hex string
const pngName = generateHexStringName(10);
// Remove any existing file with the same name in order to prevent confusion
removeExistingFile(pngName);
var view = new vega.View(vega.parse(statsObject), {renderer: 'none'});
// Generate an SVG string
view.toSVG().then(async function (svg) {
// Working SVG string
console.log(svg);
// Process obtained SVG string, e. g. write it to PNG file
}).catch(function(err) {
console.error(err);
});
// Return the name of the generated PNG file
return pngName;
}
// Using view.toSVG() along with the npm package sharp worked well for me
const view = new vega.View(vega.parse(templateObject), {renderer: 'none'});
view.toSVG().then(async function (svg) {
await sharp(Buffer.from(svg))
.toFormat('png')
.toFile('fileName.png')
}).catch(function(err) {
console.error(err);
});
我已经在 Discord 机器人上工作了几个星期,它基本上是在服务器上编译统计数据并推断模式。为了改进它,我想让它生成 PNG 格式的图形,以便将它们发送回用户 - 简而言之,没有 DOM。 为了实现这一点,我目前正在使用 vega(5.10.1 版 - 最新版)和 node-canvas(2.6.1 版 - 最新版),以及 nodejs v12.16.1.
我一直在网上搜索有关 vega 使用的帮助,发现了一些相互矛盾的来源。我一直在使用此处提供的示例代码: https://vega.github.io/vega/usage/
问题是我不断收到此错误:
TypeError: Cannot read property 'getContext' of null
message:"Cannot read property 'getContext' of null"
stack:"TypeError: Cannot read property 'getContext' of null
at resize (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-scenegraph\build\vega-scenegraph.js:3665:28)
at CanvasRenderer.prototype.resize (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-scenegraph\build\vega-scenegraph.js:3714:5)
at CanvasRenderer.prototype.initialize (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-scenegraph\build\vega-scenegraph.js:3294:17)
at CanvasRenderer.prototype.initialize (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-scenegraph\build\vega-scenegraph.js:3709:28)
at initializeRenderer (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-view\build\vega-view.js:657:8)
at renderHeadless (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-view\build\vega-view.js:780:12)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async View.renderToCanvas [as toCanvas] (e:\DEV\GIT REPOS\GITHUB\P...
这是给我带来麻烦的代码:
// Imports
const vega = require('vega');
// Render image from given graph spec (statsObject)
async function graphToImage (statsObject) {
graphObject = new vega.View(vega.parse(statsObject), { renderer: 'none'});
const pngName = generateFileName(10);
removeExistingFile(pngName);
graphObject.toCanvas().then(canvas => {
console.log('Writing PNG to file...');
writeFile(`../../../../generated/${pngName}.png`, canvas.toBuffer());
}).catch(err => {
console.log("Error writing PNG to file:");
console.error(err);
});
return pngName;
}
我真的不知道 canvas 或 vega 是如何工作的,所以我不知道是什么导致了这个问题以及如何解决它...但是,问题似乎出在里面toCanvas()
方法。非常感谢任何帮助!
提前致谢!
编辑:我设法解决了我的问题,我在这里发布答案以供将来通知:
我通过使用 view.toSVG()
而不是错误的 view.toCanvas()
将 View 对象直接渲染为 SVG 字符串,成功地生成了一张图表图片,效果很好。
然后,剩下要做的就是将得到的SVG字符串转换成PNG文件,就这样了。
这是更新后的工作代码:
// Imports
const vega = require('vega');
// Render image from given graph object
async function graphToImage (statsObject) {
// Generate a new 10-char hex string
const pngName = generateHexStringName(10);
// Remove any existing file with the same name in order to prevent confusion
removeExistingFile(pngName);
var view = new vega.View(vega.parse(statsObject), {renderer: 'none'});
// Generate an SVG string
view.toSVG().then(async function (svg) {
// Working SVG string
console.log(svg);
// Process obtained SVG string, e. g. write it to PNG file
}).catch(function(err) {
console.error(err);
});
// Return the name of the generated PNG file
return pngName;
}
// Using view.toSVG() along with the npm package sharp worked well for me
const view = new vega.View(vega.parse(templateObject), {renderer: 'none'});
view.toSVG().then(async function (svg) {
await sharp(Buffer.from(svg))
.toFormat('png')
.toFile('fileName.png')
}).catch(function(err) {
console.error(err);
});