问题
I am using canvg to convert a chart svg to an image. We have the issue that by default not all CSS attributes are applied to the image so we ended up using getComputedStyle in a loop.
This is a total mess under performance aspects if we have 10 or even 20 charts to be exported at once.
var labels = ['2018-10-01', '2018-10-02', '2018-10-03', '2018-10-04', '2018-10-05', '2018-10-06', '2018-10-07', '2018-10-08', '2018-10-09', '2018-10-10', '2018-10-11', '2018-10-12', '2018-10-13', '2018-10-14', '2018-10-15', '2018-10-16', '2018-10-17', '2018-10-18', '2018-10-19', '2018-10-20', '2018-10-21', '2018-10-22', '2018-10-23', '2018-10-24', '2018-10-25', '2018-10-26', '2018-10-27', '2018-10-28', '2018-10-29', '2018-10-30', '2018-10-31', '2018-11-01', '2018-11-02', '2018-11-03', '2018-11-04', '2018-11-05', '2018-11-06', '2018-11-07', '2018-11-08', '2018-11-09', '2018-11-10', '2018-11-11', '2018-11-12', '2018-11-13', '2018-11-14', '2018-11-15', '2018-11-16', '2018-11-17', '2018-11-18', '2018-11-19', '2018-11-20', '2018-11-21', '2018-11-22', '2018-11-23', '2018-11-24', '2018-11-25', '2018-11-26', '2018-11-27', '2018-11-28', '2018-11-29', '2018-11-30', '2018-12-01', '2018-12-02', '2018-12-03', '2018-12-04', '2018-12-05', '2018-12-06', '2018-12-07', '2018-12-08', '2018-12-09', '2018-12-10', '2018-12-11', '2018-12-12', '2018-12-13', '2018-12-14', '2018-12-15', '2018-12-16', '2018-12-17', '2018-12-18', '2018-12-19', '2018-12-20', '2018-12-21', '2018-12-22', '2018-12-23', '2018-12-24', '2018-12-25', '2018-12-26', '2018-12-27', '2018-12-28', '2018-12-29', '2018-12-30', '2018-12-31', '2019-01-01', '2019-01-02', '2019-01-03', '2019-01-04', '2019-01-05', '2019-01-06', '2019-01-07', '2019-01-08', '2019-01-09', '2019-01-10', '2019-01-11', '2019-01-12', '2019-01-13', '2019-01-14', '2019-01-15', '2019-01-16', '2019-01-17', '2019-01-18', '2019-01-19', '2019-01-20', '2019-01-21', '2019-01-22', '2019-01-23', '2019-01-24', '2019-01-25', '2019-01-26', '2019-01-27', '2019-01-28', '2019-01-29', '2019-01-30', '2019-01-31', '2019-02-01', '2019-02-02', '2019-02-03', '2019-02-04', '2019-02-05', '2019-02-06', '2019-02-07', '2019-02-08', '2019-02-09', '2019-02-10', '2019-02-11', '2019-02-12', '2019-02-13', '2019-02-14', '2019-02-15', '2019-02-16', '2019-02-17', '2019-02-18', '2019-02-19', '2019-02-20', '2019-02-21', '2019-02-22', '2019-02-23', '2019-02-24', '2019-02-25', '2019-02-26', '2019-02-27', '2019-02-28'];
var columns = ['data101', 'data2', 'data347'];
var data = [
[0, 0, 2, 2, 1, 2, 7, 3, 1, 7, 5, 5, 5, 5, 6, 6, 11, 7, 2, 7, 16, 7, 3, 5, 10, 9, 11, 7, 3, 7, 7, 10, 10, 9, 18, 10, 20, 13, 9, 19, 16, 13, 20, 18, 14, 15, 18, 20, 19, 11, 13, 13, 12, 16, 11, 12, 21, 20, 23, 19, 19, 23, 23, 24, 23, 25, 21, 23, 20, 22, 21, 23, 24, 25, 27, 29, 28, 25, 24, 17, 20, 24, 22, 27, 21, 27, 19, 26, 31, 27, 28, 27, 21, 20, 27, 22, 22, 19, 17, 21, 23, 19, 22, 20, 21, 25, 15, 19, 20, 19, 21, 28, 17, 20, 14, 18, 17, 20, 27, 21, 18, 18, 20, 16, 27, 16, 16, 9, 18, 8, 19, 13, 8, 16, 15, 16, 9, 15, 10, 13, 10, 11, 10, 13, 12, 7, 14, 16, 13, 14, 8],
[0, 0, 338, 1201, 1268, 1371, 1286, 1148, 446, 288, 228, 253, 193, 201, 283, 393, 436, 379, 421, 444, 444, 417, 513, 353, 364, 399, 238, 191, 305, 337, 365, 349, 365, 244, 101, 39, 55, 72, 151, 98, 31, 127, 114, 92, 104, 196, 307, 245, 84, 168, 41, 38, 292, 488, 536, 569, 495, 448, 408, 358, 344, 380, 328, 334, 332, 330, 345, 312, 369, 377, 356, 301, 226, 273, 237, 116, 178, 133, 114, 138, 95, 143, 74, 74, 83, 47, 75, 101, 96, 59, 46, 128, 70, 57, 93, 80, 94, 93, 63, 86, 81, 63, 70, 102, 91, 67, 69, 68, 88, 76, 79, 70, 119, 88, 74, 94, 76, 54, 82, 90, 75, 130, 67, 78, 106, 91, 81, 27, 77, 21, 104, 83, 55, 60, 62, 304, 393, 191, 292, 77, 76, 55, 125, 89, 99, 127, 60, 75, 99, 120, 56],
[0, 0, 0, 1419, 7454, 12638, 10944, 7652, 4272, 11219, 9071, 7207, 7929, 8373, 9566, 6310, 7406, 9286, 8415, 7659, 6457, 3380, 10902, 10952, 10508, 7219, 4625, 4484, 4396, 5178, 5991, 7927, 14132, 14307, 5094, 10011, 6257, 9184, 18574, 12597, 11415, 7118, 9991, 10225, 14337, 4417, 12701, 17833, 23553, 10037, 4833, 5894, 19421, 14735, 12597, 8730, 5888, 11836, 13143, 17219, 10492, 10528, 8649, 11868, 10502, 6758, 7672, 8479, 11142, 22330, 26595, 4423, 17434, 8709, 9657, 7823, 9135, 19765, 18016, 16010, 8419, 7300, 8877, 9611, 9050, 8680, 8211, 6635, 3069, 10739, 6288, 6761, 7807, 16243, 20415, 23051, 19727, 8721, 6445, 8585, 13688, 14728, 17113, 16255, 3898, 4622, 3869, 3774, 4190, 3461, 4824, 4608, 4613, 3677, 3648, 3575, 3556, 4036, 3732, 2517, 4676, 4129, 3250, 4142, 3987, 4396, 3362, 2964, 1849, 2609, 2851, 3003, 3583, 3473, 3190, 2658, 4363, 3959, 4588, 3771, 4315, 3178, 3354, 3159, 2695, 4114, 4292, 3322, 1218, 3526, 3717]
];
var colors = ['#0065A3', '#767670', '#D73648', '#7FB2CE', '#00345B'];
var padding = 5;
//prepare chart data
var columnData = [];
var chartDataColumns = [];
var chartData = [];
chartData.push([columns[0]].concat(data[0]));
chartDataColumns = [
['x'].concat(labels)
].concat(chartData);
var chart1 = c3.generate({
bindto: d3.select('#chart1'),
data: {
x: 'x',
columns: [['x'].concat(labels)].concat(chartData),
type: 'line',
onmouseover: function(d) {
chart1.focus(d.id);
chart2.focus(d.id);
},
onmouseout: function() {
chart1.revert();
chart2.revert();
}
},
legend: {
position: 'right',
show: true,
item: {
onclick: function(id) {
if (chart1) chart1.toggle(id);
if (chart2) chart2.toogle(id);
},
onmouseover: function(id) {
if (chart1) chart1.focus(id);
if (chart2) chart2.focus(id);
},
onmouseout: function(id) {
if (chart1) chart1.revert();
if (chart2) chart2.revert();
}
}
},
tooltip: {
show: true,
format: {
value: function(value) {
return d3.format(",.0f")(value);
}
}
},
zoom: {
enabled: true
},
axis: {
x: {
type: 'timeseries',
tick: {
rotate: 90,
format: '%Y-%m-%d'
}
},
y: {
label: 'sample-data',
tick: {
format: d3.format(",")
}
}
},
color: {
pattern: colors
}
});
var chart2 = c3.generate({
bindto: d3.select('#chart2'),
data: {
columns: [[columns[0]].concat(data[0])],
type: 'pie',
onmouseover: function(id) {
if (chart1) chart1.focus(id);
if (chart2) chart2.focus(id);
},
onmouseout: function(id) {
if (chart1) chart1.revert();
if (chart2) chart2.revert();
}
},
legend: {
position: 'right',
show: true,
item: {
onclick: function(id) {
if (chart1) chart1.toggle(id);
if (chart2) chart2.toogle(id);
},
onmouseover: function(id) {
if (chart1) chart1.focus(id);
if (chart2) chart2.focus(id);
},
onmouseout: function(id) {
if (chart1) chart1.revert();
if (chart2) chart2.revert();
}
}
},
color: {
pattern: colors
},
});
for (var i = 1; i < columns.length; i++) {
setTimeout(function(column) {
chart1.load({
columns: [
[columns[column]].concat(data[column])
]
});
chart2.load({
columns: [[columns[column]].concat(data[column])]
});
}, (i * 5000 / columns.length), i);
}
document.getElementById("exportButton").onclick = function() {
exportChartToImage();
};
function exportChartToImage() {
var createImagePromise = new Promise(function(resolve, reject) {
var images = [];
d3.selectAll('svg').each(function() {
if (this.parentNode) {
images.push(getSvgImage(this.parentNode, true));
}
});
if (images.length > 0)
resolve(images);
else
reject(images);
});
createImagePromise.then(function(images) {
images.forEach(function(img, n) {
img.toBlob(function(blob) {
saveAs(blob, "image_" + (n + 1) + ".png");
});
});
})
.catch(function(error) {
throw error;
});
};
/**
* Converts a SVG-Chart to a canvas and returns it.
*/
function getSvgImage(svgContainer, png) {
var svgEl = d3.select(svgContainer).select('svg').node();
var svgCopyEl = svgEl.cloneNode(true);
if (!svgCopyEl)
return;
//remove elements not for printing
lensObject = d3.selectAll(".hidden-print").remove().exit();
//add temp document objects
var emptySvgEl = d3.select(document.createElementNS("http://www.w3.org/2000/svg", "svg")).attr("id", "emptysvg")
.attr("version", 1.1)
.attr("height", 2)
.node();
var canvasComputed = d3.select(document.createElement("canvas")).attr("id", "canvasComputed").node();
var container = d3.select(document.createElement("div")).attr("style", "display: none;")
.attr("class", "c3").node();
svgContainer.append(container);
container.append(svgCopyEl);
container.append(emptySvgEl);
container.append(canvasComputed);
//apply all CSS styles to SVG
exportStyles(svgCopyEl, emptySvgEl);
// transform SVG to canvas using external canvg
canvg(canvasComputed, new XMLSerializer().serializeToString(svgCopyEl));
//remove temp document objects
canvasComputed.remove();
emptySvgEl.remove();
svgCopyEl.remove();
container.remove();
return canvasComputed;
}
function exportStyles(svg, emptySvg) {
var tree = [];
var emptySvgDeclarationComputed = getComputedStyle(emptySvg);
//d3.select(svg).selectAll().each(function() {
$(svg).find("*").each(function() {
explicitlySetStyle(this, emptySvgDeclarationComputed);
});
}
function traverse(obj, tree) {
tree.push(obj);
if (obj.hasChildNodes()) {
var child = obj.firstChild;
while (child) {
if (child.nodeType === 1 && child.nodeName != 'SCRIPT') {
traverse(child, tree);
}
child = child.nextSibling;
}
}
return tree;
}
function explicitlySetStyle(element, emptySvgDeclarationComputed) {
var cSSStyleDeclarationComputed = getComputedStyle(element);
var i, len, key, value;
var computedStyleStr = "";
for (i = 0, len = cSSStyleDeclarationComputed.length; i < len; i++) {
key = cSSStyleDeclarationComputed[i];
value = cSSStyleDeclarationComputed.getPropertyValue(key);
if (value !== emptySvgDeclarationComputed.getPropertyValue(key)) {
if (key == 'visibility' && value == 'hidden') {
computedStyleStr += 'display: none;';
} else {
computedStyleStr += key + ":" + value + ";";
}
}
}
element.setAttribute('style', computedStyleStr);
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.6.12/c3.min.css" rel="stylesheet" />
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.6.12/c3.min.js"></script>
<!-- Required to convert named colors to RGB -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/canvg/1.4/rgbcolor.min.js"></script>
<!-- Optional if you want blur -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/stackblur-canvas/1.4.1/stackblur.min.js"></script>
<!-- Main canvg code -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/canvg/1.5/canvg.js"></script>
<script src="https://fastcdn.org/FileSaver.js/1.1.20151003/FileSaver.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="chart1" class "c3">
</div>
<div id="chart2" class="c3">
</div>
<button type="button" id="exportButton">
export to SVG
</button>
=> http://jsfiddle.net/gothmogg/jLt3yq75/
This sample shows exemplary how we apply the CSS styles to the SVG. If we use normal canvg without getComputedStyle the x and y axes of the c3 chart look like a total mess...
Do you know faster way to get valid images? Is there a way to filter the CSS-styles? Maybe use "c3" styles only?
回答1:
I have managed to solve the performance problems of exporting C3 SVG charts to PNG by avoiding to use getComputedStyle.
Browsing the issues in C3 I found issue #313
https://github.com/c3js/c3/issues/313
For others http://www.nihilogic.dk/labs/canvas2image/ might also be a good place to look at but I found a solution at https://gist.github.com/aendrew/1ad2eed6afa29e30d52e#file-exportchart-js-L29.
I have changed the code from using angular to d3 and now it works (for me).
Hopefully this might help others having the same issue.
Here the working code. Please note: the css styles are only examined and inlined once.
var labels = ['2018-10-01', '2018-10-02', '2018-10-03', '2018-10-04', '2018-10-05', '2018-10-06', '2018-10-07', '2018-10-08', '2018-10-09', '2018-10-10', '2018-10-11', '2018-10-12', '2018-10-13', '2018-10-14', '2018-10-15', '2018-10-16', '2018-10-17', '2018-10-18', '2018-10-19', '2018-10-20', '2018-10-21', '2018-10-22', '2018-10-23', '2018-10-24', '2018-10-25', '2018-10-26', '2018-10-27', '2018-10-28', '2018-10-29', '2018-10-30', '2018-10-31', '2018-11-01', '2018-11-02', '2018-11-03', '2018-11-04', '2018-11-05', '2018-11-06', '2018-11-07', '2018-11-08', '2018-11-09', '2018-11-10', '2018-11-11', '2018-11-12', '2018-11-13', '2018-11-14', '2018-11-15', '2018-11-16', '2018-11-17', '2018-11-18', '2018-11-19', '2018-11-20', '2018-11-21', '2018-11-22', '2018-11-23', '2018-11-24', '2018-11-25', '2018-11-26', '2018-11-27', '2018-11-28', '2018-11-29', '2018-11-30', '2018-12-01', '2018-12-02', '2018-12-03', '2018-12-04', '2018-12-05', '2018-12-06', '2018-12-07', '2018-12-08', '2018-12-09', '2018-12-10', '2018-12-11', '2018-12-12', '2018-12-13', '2018-12-14', '2018-12-15', '2018-12-16', '2018-12-17', '2018-12-18', '2018-12-19', '2018-12-20', '2018-12-21', '2018-12-22', '2018-12-23', '2018-12-24', '2018-12-25', '2018-12-26', '2018-12-27', '2018-12-28', '2018-12-29', '2018-12-30', '2018-12-31', '2019-01-01', '2019-01-02', '2019-01-03', '2019-01-04', '2019-01-05', '2019-01-06', '2019-01-07', '2019-01-08', '2019-01-09', '2019-01-10', '2019-01-11', '2019-01-12', '2019-01-13', '2019-01-14', '2019-01-15', '2019-01-16', '2019-01-17', '2019-01-18', '2019-01-19', '2019-01-20', '2019-01-21', '2019-01-22', '2019-01-23', '2019-01-24', '2019-01-25', '2019-01-26', '2019-01-27', '2019-01-28', '2019-01-29', '2019-01-30', '2019-01-31', '2019-02-01', '2019-02-02', '2019-02-03', '2019-02-04', '2019-02-05', '2019-02-06', '2019-02-07', '2019-02-08', '2019-02-09', '2019-02-10', '2019-02-11', '2019-02-12', '2019-02-13', '2019-02-14', '2019-02-15', '2019-02-16', '2019-02-17', '2019-02-18', '2019-02-19', '2019-02-20', '2019-02-21', '2019-02-22', '2019-02-23', '2019-02-24', '2019-02-25', '2019-02-26', '2019-02-27', '2019-02-28'];
var columns = ['data101', 'data2', 'data347'];
var data = [
[0, 0, 2, 2, 1, 2, 7, 3, 1, 7, 5, 5, 5, 5, 6, 6, 11, 7, 2, 7, 16, 7, 3, 5, 10, 9, 11, 7, 3, 7, 7, 10, 10, 9, 18, 10, 20, 13, 9, 19, 16, 13, 20, 18, 14, 15, 18, 20, 19, 11, 13, 13, 12, 16, 11, 12, 21, 20, 23, 19, 19, 23, 23, 24, 23, 25, 21, 23, 20, 22, 21, 23, 24, 25, 27, 29, 28, 25, 24, 17, 20, 24, 22, 27, 21, 27, 19, 26, 31, 27, 28, 27, 21, 20, 27, 22, 22, 19, 17, 21, 23, 19, 22, 20, 21, 25, 15, 19, 20, 19, 21, 28, 17, 20, 14, 18, 17, 20, 27, 21, 18, 18, 20, 16, 27, 16, 16, 9, 18, 8, 19, 13, 8, 16, 15, 16, 9, 15, 10, 13, 10, 11, 10, 13, 12, 7, 14, 16, 13, 14, 8],
[0, 0, 338, 1201, 1268, 1371, 1286, 1148, 446, 288, 228, 253, 193, 201, 283, 393, 436, 379, 421, 444, 444, 417, 513, 353, 364, 399, 238, 191, 305, 337, 365, 349, 365, 244, 101, 39, 55, 72, 151, 98, 31, 127, 114, 92, 104, 196, 307, 245, 84, 168, 41, 38, 292, 488, 536, 569, 495, 448, 408, 358, 344, 380, 328, 334, 332, 330, 345, 312, 369, 377, 356, 301, 226, 273, 237, 116, 178, 133, 114, 138, 95, 143, 74, 74, 83, 47, 75, 101, 96, 59, 46, 128, 70, 57, 93, 80, 94, 93, 63, 86, 81, 63, 70, 102, 91, 67, 69, 68, 88, 76, 79, 70, 119, 88, 74, 94, 76, 54, 82, 90, 75, 130, 67, 78, 106, 91, 81, 27, 77, 21, 104, 83, 55, 60, 62, 304, 393, 191, 292, 77, 76, 55, 125, 89, 99, 127, 60, 75, 99, 120, 56],
[0, 0, 0, 1419, 7454, 12638, 10944, 7652, 4272, 11219, 9071, 7207, 7929, 8373, 9566, 6310, 7406, 9286, 8415, 7659, 6457, 3380, 10902, 10952, 10508, 7219, 4625, 4484, 4396, 5178, 5991, 7927, 14132, 14307, 5094, 10011, 6257, 9184, 18574, 12597, 11415, 7118, 9991, 10225, 14337, 4417, 12701, 17833, 23553, 10037, 4833, 5894, 19421, 14735, 12597, 8730, 5888, 11836, 13143, 17219, 10492, 10528, 8649, 11868, 10502, 6758, 7672, 8479, 11142, 22330, 26595, 4423, 17434, 8709, 9657, 7823, 9135, 19765, 18016, 16010, 8419, 7300, 8877, 9611, 9050, 8680, 8211, 6635, 3069, 10739, 6288, 6761, 7807, 16243, 20415, 23051, 19727, 8721, 6445, 8585, 13688, 14728, 17113, 16255, 3898, 4622, 3869, 3774, 4190, 3461, 4824, 4608, 4613, 3677, 3648, 3575, 3556, 4036, 3732, 2517, 4676, 4129, 3250, 4142, 3987, 4396, 3362, 2964, 1849, 2609, 2851, 3003, 3583, 3473, 3190, 2658, 4363, 3959, 4588, 3771, 4315, 3178, 3354, 3159, 2695, 4114, 4292, 3322, 1218, 3526, 3717]
];
var colors = ['#0065A3', '#767670', '#D73648', '#7FB2CE', '#00345B'];
var padding = 5;
//prepare chart data
var columnData = [];
var chartDataColumns = [];
var chartData = [];
var C3Styles = null;
chartData.push([columns[0]].concat(data[0]));
chartDataColumns = [
['x'].concat(labels)
].concat(chartData);
var chart1 = c3.generate({
bindto: d3.select('#chart1'),
data: {
x: 'x',
columns: [
['x'].concat(labels)
].concat(chartData),
type: 'line',
onmouseover: function(d) {
chart1.focus(d.id);
chart2.focus(d.id);
},
onmouseout: function() {
chart1.revert();
chart2.revert();
}
},
legend: {
position: 'right',
show: true,
item: {
onclick: function(id) {
if (chart1) chart1.toggle(id);
if (chart2) chart2.toogle(id);
},
onmouseover: function(id) {
if (chart1) chart1.focus(id);
if (chart2) chart2.focus(id);
},
onmouseout: function(id) {
if (chart1) chart1.revert();
if (chart2) chart2.revert();
}
}
},
tooltip: {
show: true,
format: {
value: function(value) {
return d3.format(",.0f")(value);
}
}
},
zoom: {
enabled: true
},
axis: {
x: {
type: 'timeseries',
tick: {
rotate: 90,
format: '%Y-%m-%d'
}
},
y: {
label: 'sample-data',
tick: {
format: d3.format(",")
}
}
},
color: {
pattern: colors
}
});
var chart2 = c3.generate({
bindto: d3.select('#chart2'),
data: {
columns: [
[columns[0]].concat(data[0])
],
type: 'pie',
onmouseover: function(id) {
if (chart1) chart1.focus(id);
if (chart2) chart2.focus(id);
},
onmouseout: function(id) {
if (chart1) chart1.revert();
if (chart2) chart2.revert();
}
},
legend: {
position: 'right',
show: true,
item: {
onclick: function(id) {
if (chart1) chart1.toggle(id);
if (chart2) chart2.toogle(id);
},
onmouseover: function(id) {
if (chart1) chart1.focus(id);
if (chart2) chart2.focus(id);
},
onmouseout: function(id) {
if (chart1) chart1.revert();
if (chart2) chart2.revert();
}
}
},
color: {
pattern: colors
},
});
for (var i = 1; i < columns.length; i++) {
setTimeout(function(column) {
chart1.load({
columns: [
[columns[column]].concat(data[column])
]
});
chart2.load({
columns: [
[columns[column]].concat(data[column])
]
});
}, (i * 5000 / columns.length), i);
}
document.getElementById("exportButton").onclick = function() {
exportChartToImage();
};
function exportChartToImage() {
var createImagePromise = new Promise(function(resolve, reject) {
var images = [];
d3.selectAll('svg').each(function() {
if (this.parentNode) {
images.push(getSvgImage(this.parentNode, true));
}
});
if (images.length > 0)
resolve(images);
else
reject(images);
});
createImagePromise.then(function(images) {
images.forEach(function(img, n) {
img.toBlob(function(blob) {
saveAs(blob, "image_" + (n + 1) + ".png");
});
});
})
.catch(function(error) {
throw error;
});
};
/**
* Converts a SVG-Chart to a canvas and returns it.
*/
function getSvgImage(svgContainer, png) {
var svgEl = d3.select(svgContainer).select('svg').node();
var svgCopyEl = svgEl.cloneNode(true);
if (!svgCopyEl)
return;
//remove elements not for printing
lensObject = d3.selectAll(".hidden-print").remove().exit();
//add temp document objects
var canvasComputed = d3.select(document.createElement("canvas")).attr("id", "canvasComputed").node();
var container = d3.select(document.createElement("div")).attr("style", "display: none;")
.attr("class", "c3").node();
svgContainer.append(container);
container.append(svgCopyEl);
container.append(canvasComputed);
/* taken from https://gist.github.com/aendrew/1ad2eed6afa29e30d52e#file-exportchart-js
and changed from, angular to D3 functions
*/
/* Take styles from CSS and put as inline SVG attributes so that Canvg
can properly parse them. */
var chartStyle;
if (!C3Styles) {
// Get rules from c3.css
var styleSheets = document.styleSheets;
for (var i = 0; i <= styleSheets.length - 1; i++) {
if (styleSheets[i].href && (styleSheets[i].href.indexOf('c3.min.css') !== -1 || styleSheets[i].href.indexOf('c3.css') !== -1)) {
try {
if (styleSheets[i].rules !== undefined) {
chartStyle = styleSheets[i].rules;
} else {
chartStyle = styleSheets[i].cssRules;
}
break;
}
//Note that SecurityError exception is specific to Firefox.
catch (e) {
if (e.name == 'SecurityError') {
console.log("SecurityError. Cant read: " + styleSheets[i].href);
continue;
}
}
}
if (chartStyle !== null && chartStyle !== undefined) {
C3Styles = {};
var selector;
// Inline apply all the CSS rules as inline
for (i = 0; i < chartStyle.length; i++) {
if (chartStyle[i].type === 1) {
selector = chartStyle[i].selectorText;
var styleDec = chartStyle[i].style;
for (var s = 0; s < styleDec.length; s++) {
C3Styles[styleDec[s]] = styleDec[styleDec[s]];
}
}
}
}
}
}
if (C3Styles)
d3.select(svgCopyEl).selectAll('.c3:not(.c3-chart):not(path)').style(C3Styles);
// SVG doesn't use CSS visibility and opacity is an attribute, not a style property. Change hidden stuff to "display: none"
d3.select(svgCopyEl).selectAll('*')
.filter(function(d) {
return d && d.style && (d.style('visibility') === 'hidden' || d.style('opacity') === '0');
})
.style('display', 'none');
//fix weird back fill
d3.select(svgCopyEl).selectAll("path").attr("fill", "none");
//fix no axes
d3.select(svgCopyEl).selectAll("path.domain").attr("stroke", "black");
//fix no tick
d3.select(svgCopyEl).selectAll(".tick line").attr("stroke", "black");
// transform SVG to canvas using external canvg
canvg(canvasComputed, new XMLSerializer().serializeToString(svgCopyEl));
//remove temp document objects
canvasComputed.remove();
svgCopyEl.remove();
container.remove();
return canvasComputed;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.6.12/c3.min.css" rel="stylesheet" />
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.6.12/c3.min.js"></script>
<!-- Required to convert named colors to RGB -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/canvg/1.4/rgbcolor.min.js"></script>
<!-- Optional if you want blur -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/stackblur-canvas/1.4.1/stackblur.min.js"></script>
<!-- Main canvg code -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/canvg/1.5/canvg.js"></script>
<script src="https://fastcdn.org/FileSaver.js/1.1.20151003/FileSaver.min.js"></script>
<div id="chart1" class "c3">
</div>
<div id="chart2" class="c3">
</div>
<button type="button" id="exportButton">
export to SVG
</button>
Unfortunately
document.styleSheets[i].rules
can't be accessed in the script here. It worked in my environment though. Any idea why this does not work here?
来源:https://stackoverflow.com/questions/54944691/svg-to-image-export-performance-issues-using-canvg-xmlserializer-getcompute