我正在寻求一些有关在 NodeJS 控制台应用程序中生成 D3js 图表并将图表以 PNG 或 JPG 格式保存到磁盘的帮助。
我的用例涉及一个自动化任务,该任务每两周运行一次并循环遍历结果列表,向客户生成电子邮件通知。在那封电子邮件中,我想包含使用 D3js 生成的各种图表。要将 D3js 图表嵌入到我们的客户电子邮件中,我们需要将 D3js 图表以 PNG/JPG 格式保存到磁盘/流中,以将图像上传到我们的 SMTP 提供商。图表和数据对于每个客户来说都是动态且唯一的,因此需要在将电子邮件发送给客户之前立即生成图表。
我见过 SaaS 产品可以通过 API 生成 D3js 图表,但管理层已经排除了这种可能性 (:shrug:)。
我在网上找到的非 SaaS D3js 示例主要围绕将图表嵌入到网页中。这不是我们想要做的。
我不知道从哪里开始,这就是为什么本文中没有代码示例的原因。任何帮助将不胜感激。
d3.js 操作 HTML DOM。因此,它需要浏览器中存在的 DOM JavaScript API。
如果你想在 Node.js 中使用 d3.js,你可以使用像
jsdom
这样的库来模拟 DOM 及其 API。
sharp
包将 SVG 转换为 PNG 或任何图像文件。
// use jsdom to emulate the browser DOM
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
// for saving the SVG image to disk
const fs = require('fs');
// for converting SVG to an image
const sharp = require('sharp');
/** renders a D3.js generated SVG DOM into an image file */
const renderD3ImageFile = async (data) => {
/************************************************/
/************** Emulating the DOM ***************/
/************************************************/
// emulate the DOM structure
const dom = new JSDOM('<!DOCTYPE html><body><svg id="bar-chart"></svg></body></html>')
// get the SVG container element
const svgElement = dom.window.document.getElementById("bar-chart");
/************************************************/
/****************** D3 Drawing ******************/
/************************************************/
/* Import the d3 library
If your node app is of 'module' type, you can simply use the syntax "import * as d3 from 'd3'"
Earlier versions of d3 can be loaded using commonJs.
*/
const d3 = await import('d3');
// select the SVG element using D3.js
const svg = d3.select(svgElement)
// not sure if this is necessary
.attr('xmlns', "http://www.w3.org/2000/svg");
const svgWidth = 1080;
const svgHeight = 300;
/* Sizing the svg image
Since our DOM is emulated, we cannot enquire any sizing information from it.
We can either size the SVG by (1) providing fixed height/width values or (2) by using the viewBox attribute.
The following code uses the viewBox attributes
*/
svg.attr('width', `${svgWidth}px`)
.attr('height', `${svgHeight}px`);
// any kind of D3.js magic
const x = d3.scaleLinear([0, 100], [0, svgWidth]);
const coreGroup = svg.append('g').attr('class', 'core-group');
const barHeight = 50;
const barTopMargin = 10;
const someRandomBars = coreGroup.selectAll('g.bar')
.data([10, 75, 100, 24, 55])
.enter()
.append('g')
.attr('class', 'bar')
.attr('transform', (d,i) => `translate(0 ${i * (barHeight + barTopMargin)})`);
someRandomBars.append('rect')
.attr('x', 0).attr('y', 0)
.attr('width', (d) => {
return x(d) + 'px';
})
.attr('height', `${barHeight}px`)
// all styling has to be done inline
.attr('fill', 'purple');
// the html of the element is our SVG image
const svgAsText = svgElement.outerHTML;
// save as SVG (for debugging)
fs.writeFileSync('output.svg', svgAsText);
// save as raster image file
const svgImageBuffer = Buffer.from(svgAsText);
await sharp(svgImageBuffer)
.png()
.toFile("output.png");
};
// trigger the render promise
renderD3ImageFile()
.then(() => console.log('picture rendered!'))
.catch(error => console.error(error));
此代码创建一个简单的条形图 PNG 图像:
最新版本的 d3.js 是一个 ES 模块。因此,除非您的 Node.js 应用程序要
"type": "module"
,否则您必须通过 import('d3')
承诺加载它。
许多示例都将 CommonJS 与早期版本的 d3 结合使用。
有两种方法可以调整 SVG 绘图的大小:
width
和 height
属性(如上例所示)。viewBox
与 preserveAspectRatio
属性一起使用。我喜欢使用它,因为它为将来的图像放置提供了更多的灵活性。重要的是要知道 DOM 只是模拟的。 IE。查询大小的 DOM 函数 (
getBBox()
) 不存在。
在为 HTML 渲染 SVG 时,我们可以(并且应该)使用 CSS 样式。在 Node.js 中渲染 SVG 时,我们必须内联部署所有样式。 当尝试在客户端和服务器端之间共享 d3.js 代码时,这可能是一个问题。