我已尝试遵循此处的建议:
以及 this npm module将我的 c3(基于 d3)绘图导出到图像文件 - 现在为 .png。
在浏览器中,图像文件如下所示:
但是,save-svg-as-png
节点模块会生成以下内容:
上述 SO 帖子中的脚本在新选项卡中生成以下内容:
如您所见,路径是闭合的,然后被填充,就好像 .css 被忽略了一样。
这是生成图的代码:
# plotMultiline.js
import * as c3 from 'c3';
import * as d3 from 'd3';
import { saveSvgAsPng } from 'save-svg-as-png';
import createImageFromSVG from './createImageFromSVG';
const plotMultiline = (
chartID, dataToPlot,
headingsAndLabels,
xHeading,
nTicks,
xRange,
xLines = [],
title,
xAxisTitle,
note,
yTitle = 'Frequency of occurrence',
inset = null,
) => {
d3.select('body').append('div')
.attr('id', chartID);
const yDatas = Object.entries(headingsAndLabels).map(([columnName, newLabel]) =>
[newLabel, ...dataToPlot.map(d => d[columnName])]);
const firstKey = Object.keys(dataToPlot[0])[0];
const secondKey = Object.keys(dataToPlot[0])[1];
const xMin = +dataToPlot[0][firstKey];
const xMax = +[...dataToPlot].pop()[secondKey];
const xTickValuesAll = [...dataToPlot.map(d => +d[firstKey])];
const nXTickValuesAll = xTickValuesAll.length;
const xTickValuesIndices =
[...Array(nTicks).keys()].map(d => d * Math.ceil(nXTickValuesAll / nTicks))
.filter(d => d <= nXTickValuesAll - 1);
let xTickValues = [];
if (nTicks) {
if (typeof nTicks === 'number') {
xTickValues = [...xTickValuesIndices.map(i => +xTickValuesAll[i]), xMax];
} else if (nTicks === 'integer') {
xTickValues = [...xTickValuesAll, xMax].filter(d => Math.round(d) === d);
}
} else {
xTickValues = [...xTickValuesAll, xMax];
}
const rightPadding = (xTickValues[1] - xTickValues[0]) / 5;
const chart = c3.generate({
bindto: `#${chartID}`,
title: {
text: title,
},
point: {
show: false,
},
size: {
width: 960,
height: 500,
},
padding: {
bottom: 20,
top: 20,
},
data: {
x: xHeading,
columns: yDatas,
},
legend: {
position: 'inset',
inset,
},
axis: {
x: {
tick: {
outer: false,
values: xTickValues,
},
min: xMin,
max: xMax,
padding: { left: 0, right: rightPadding },
label: {
text: xAxisTitle || xHeading,
position: 'outer-center',
},
height: 50,
},
y: {
padding: { top: 0, bottom: 0 },
label: {
text: yTitle,
position: 'outer-middle',
},
},
},
grid: {
x: {
show: true,
lines: xLines,
},
y: {
show: true,
},
},
});
d3.select(`#${chartID} svg`).attr('id', `svg-${chartID}`);
if (note) {
d3.select(`#${chartID} svg`).append('text')
.attr('x', 630)
.attr('y', 485)
.classed('note', true)
.text(note);
}
if (xRange) {
const xRangeMin = xRange[0];
const xRangeMax = xRange[1];
chart.axis.range({
min: {
x: xRangeMin,
},
max: {
x: xRangeMax,
},
});
}
setTimeout(() => {
d3.select(`#${chartID}`)
.append('button')
.on('click', () => saveSvgAsPng(d3.select(`#svg-${chartID}`)[0]['0'], `#svg-${chartID}.png`))
.classed('btn btn-success', true)
.attr('id', 'button-library');
d3.select(`#${chartID}`)
.append('button')
.on('click', () => createImageFromSVG(`#svg-${chartID}`))
.classed('btn btn-success', true)
.attr('id', 'button-so-script');
}, 1000);
};
export default plotMultiline;
和(从上述 SO 帖子中复制):
# createImageFromSVG
import * as d3 from 'd3';
const createImageFromSVG = (selectorForSVG) => {
// get styles from all required stylesheets
// http://www.coffeegnome.net/converting-svg-to-png-with-canvg/
let style = '\n';
for (let i = 0; i < document.styleSheets.length; i++) {
const sheet = document.styleSheets[i];
if (sheet.href) {
const { rules } = sheet;
if (rules) {
for (let j = 0; j < rules.length; j++) {
style += (`${rules[j].cssText}\n`);
}
}
}
}
const svg = d3.select(selectorForSVG);
const img = new Image();
const serializer = new XMLSerializer();
// prepend style to svg
svg.insert('defs', ':first-child');
d3.select('svg defs')
.append('style')
.attr('type', 'text/css')
.html(style);
// generate IMG in new tab
const svgStr = serializer.serializeToString(svg.node());
img.src = `data:image/svg+xml;base64,${window.btoa(unescape(encodeURIComponent(svgStr)))}`;
const popUp = window.open();
if (popUp) {
popUp.document.write(`<img src="${img.src}"/>`);
}
};
export default createImageFromSVG;
我还上传了一个带有这段代码的示例元素到github:
https://github.com/shafiquejamal/export-svg-to-png
有人可以建议如何使导出的文件看起来像在浏览器中呈现的那样吗?谢谢!
更新 #1:关注 https://stackoverflow.com/users/3702797/kaiido下面的建议,在plotMultiline.js
我改了
d3.select(`#${chartID} svg`).attr('id', `svg-${chartID}`)
到
d3.select(`#${chartID} svg`).attr('id', `svg-${chartID}`)
.classed('c3', true);
然后saveSvgAsPng
生成如下图片文件:
这解决了闭合形状填充问题,但背景实际上是透明的,而不是白色,如您从以下屏幕截图中看到的那样:
但这实际上已经足够满足我的目的了。
最佳答案
这是因为 c3 从父 div 容器设置了一些规则,它有一个 class="c3"
属性。
特别是,你会发现一个
.c3 path, .c3 line {
fill: none;
stroke: rgb(0, 0, 0);
}
规则。
当您将 svg 转换为独立文件时,此规则将不再匹配。
要解决这个问题,您可以简单地在父 svg
节点上设置此类。
svg.classList.add('c3');
但是你会失去的
.c3 svg {
font: 10px sans-serif;
-webkit-tap-highlight-color: transparent;
}
规则。
所以你可能必须自己设置它,并将其转换为例如 .c3 svg, svg.c3 {...
或者,您可以getComputedStyle
您的 svg 的所有节点,并过滤掉默认节点,但这对浏览器来说仍然需要大量工作...
关于javascript - 使用 JavaScript 将 svg 导出为 png 或其他具有样式的图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47244193/