在NodeJS控制台应用程序中生成D3js图表

efzxgjgh  于 2023-10-17  发布在  Node.js
关注(0)|答案(1)|浏览(135)

我正在寻找一些关于在NodeJS控制台应用程序中生成D3 js图表并将图表保存为PNG或JPG的帮助。
我的用例涉及一个自动化任务,该任务每两周运行一次,并循环遍历结果列表,向客户生成电子邮件通知。在这封电子邮件中,我想包括使用D3 js生成的各种图表。要将D3 js图表嵌入到我们的客户电子邮件中,我们需要将D3 js图表以PNG/JPG格式保存到磁盘/流中,以便将图像上传到我们的SMTP提供程序。图表和数据是动态的,对每个客户来说都是唯一的,因此需要在向客户发送电子邮件之前立即生成图表。
我看到过SaaS产品通过API生成D3 js图表,但这被管理层排除了(:shrug:)。
我在网上找到的非SaaS D3 js示例都是围绕着将图表嵌入到网页中。我们可不想这么做。
我被从哪里开始卡住了,这就是为什么这篇文章中没有代码示例。任何帮助将不胜感激。

vfh0ocws

vfh0ocws1#

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图像:

常见问题与思考

在Node.js中加载d3.js

最新版本的d3.js是一个ES模块。因此,除非你的Node.js应用是"type": "module",否则你必须通过import('d3') promise加载它。许多示例都使用CommonJS和早期版本的d3。

调整图片大小(& elements)

有两种方法可以调整SVG图形的大小:
1.使用静态widthheight属性(如上面的示例)。
1.使用viewBoxpreserveAspectRatio属性。我喜欢使用这个,因为它提供了更多的灵活性,在未来的位置的形象。
重要的是要知道DOM只是模拟的。也就是说,查询大小(getBBox())的DOM函数不存在。

HTML vs Image渲染

当为HTML渲染SVG时,我们可以(也应该)使用CSS样式。在Node.js中渲染SVG时,我们必须内联部署所有样式。当尝试在客户端和服务器端之间共享d3.js代码时,这可能是一个问题。

相关问题