我使用amchart创建了一个图表。我稍后将把它作为PDF导出 作为suggested here,我需要先将图表转换为带有html2canvas的SVG。
它有效,但图表看起来已经破坏":
有没有办法优化结果?
以下是我的代码摘录:
/* --- Chart --- */
var chart = AmCharts.makeChart("chartdiv1", {
"type": "serial",
// ...
});
/* --- HTML2Canvas --- */
$('#cmd').click(function() {
html2canvas($("#output"), {
onrendered: function(canvas) {
document.body.appendChild(canvas);
}
});
})
PS:我知道,每个图表都有内置的导出功能。我无法使用它的原因是,在实际情况下,将会有更多内容要导出(几个图表,表格,文本等),因此我需要导出整个DIV。
答案 0 :(得分:0)
我也尝试过这样做,但如果您尝试在任何第三方图表中使用html2canvas(或任何其他库)进行打印,那么我相信大部分时间它都不起作用。有些时候由于安全原因或一些时间由于其他原因,
如果你真的想要它,那么我会建议你把它打印到某个地方,然后用html2canvas来对它进行操作,
答案 1 :(得分:0)
我尝试了很多解决方案,但我发现最多的是使用脚本转换 SVG (谢谢 @Kaiido )并使用html2canvas
插件。
我生成了您的图表和onclick
事件,我将 ID 分配给 SVG 标记。
点击按钮后, SVG 将其转换为画布。现在你可以用这个来制作你想要的东西。
请在全屏模式下尝试完全显示您的标签
$(document).ready(function(){
var chart = AmCharts.makeChart("chartdiv1", {
"type": "serial",
"theme": "light",
"dataProvider": [{
"country": "USA",
"visits": 2025
}, {
"country": "China",
"visits": 1882
}, {
"country": "Japan",
"visits": 1809
}, {
"country": "Germany",
"visits": 1322
}, {
"country": "UK",
"visits": 1122
}, {
"country": "France",
"visits": 1114
}, {
"country": "India",
"visits": 984
}, {
"country": "Spain",
"visits": 711
}, {
"country": "Netherlands",
"visits": 665
}, {
"country": "Russia",
"visits": 580
}, {
"country": "South Korea",
"visits": 443
}, {
"country": "Canada",
"visits": 441
}, {
"country": "Brazil",
"visits": 395
}],
"valueAxes": [{
"gridColor": "#FFFFFF",
"gridAlpha": 0.2,
"dashLength": 0
}],
"gridAboveGraphs": true,
"startDuration": 1,
"graphs": [{
"balloonText": "[[category]]: <b>[[value]]</b>",
"fillAlphas": 0.8,
"lineAlpha": 0.2,
"type": "column",
"valueField": "visits"
}],
"chartCursor": {
"categoryBalloonEnabled": false,
"cursorAlpha": 0,
"zoomable": false
},
"categoryField": "country",
"categoryAxis": {
"gridPosition": "start",
"gridAlpha": 0,
"tickPosition": "start",
"tickLength": 20
},
"export": {
"enabled": true
}
});
$('#cmd').click(function() {
$("svg").attr("id","svg") //Assign ID to SCG tag
// without converting the svg to png
html2canvas(chartdiv1, { // chartdiv1 is your div
onrendered: function(can) {
//dirty.appendChild(can);
}
});
// first convert your svg to png
exportInlineSVG(svg, function(data, canvas) {
svg.parentNode.replaceChild(canvas, svg);
// then call html2canvas
html2canvas(chartdiv1, { // chartdiv1 is your div
onrendered: function(can) {
can.id = 'canvas';
// clean.appendChild(can);
}
});
})
function exportInlineSVG(svg, receiver, params, quality) {
if (!svg || !svg.nodeName || svg.nodeName !== 'svg') {
console.error('Wrong arguments : should be \n exportSVG(SVGElement, function([dataURL],[canvasElement]) || IMGElement || CanvasElement [, String_toDataURL_Params, Float_Params_quality])')
return;
}
var xlinkNS = "http://www.w3.org/1999/xlink";
var clone;
// This will convert an external image to a dataURL
var toDataURL = function(image) {
var img = new Image();
// CORS workaround, this won't work in IE<11
// If you are sure you don't need it, remove the next line and the double onerror handler
// First try with crossorigin set, it should fire an error if not needed
img.crossOrigin = 'Anonymous';
img.onload = function() {
// we should now be able to draw it without tainting the canvas
var canvas = document.createElement('canvas');
var bbox = image.getBBox();
canvas.width = bbox.width;
canvas.height = bbox.height;
// draw the loaded image
canvas.getContext('2d').drawImage(this, 0, 0, bbox.width, bbox.height);
// set our original <image>'s href attribute to the dataURL of our canvas
image.setAttributeNS(xlinkNS, 'href', canvas.toDataURL());
// that was the last one
if (++encoded === total) exportDoc()
}
// No CORS set in the response
img.onerror = function() {
// save the src
var oldSrc = this.src;
// there is an other problem
this.onerror = function() {
console.warn('failed to load an image at : ', this.src);
if (--total === encoded && encoded > 0) exportDoc();
}
// remove the crossorigin attribute
this.removeAttribute('crossorigin');
// retry
this.src = '';
this.src = oldSrc;
}
// load our external image into our img
img.src = image.getAttributeNS(xlinkNS, 'href');
}
// The final function that will export our svgNode to our receiver
var exportDoc = function() {
// check if our svgNode has width and height properties set to absolute values
// otherwise, canvas won't be able to draw it
var bbox = svg.getBBox();
// avoid modifying the original one
clone = svg.cloneNode(true);
if (svg.width.baseVal.unitType !== 1) clone.setAttribute('width', bbox.width);
if (svg.height.baseVal.unitType !== 1) clone.setAttribute('height', bbox.height);
parseStyles();
// serialize our node
var svgData = (new XMLSerializer()).serializeToString(clone);
// remember to encode special chars
var svgURL = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData);
var svgImg = new Image();
svgImg.onload = function() {
// if we set a canvas as receiver, then use it
// otherwise create a new one
var canvas = (receiver && receiver.nodeName === 'CANVAS') ? receiver : document.createElement('canvas');
// IE11 doesn't set a width on svg images...
canvas.width = this.width || bbox.width;
canvas.height = this.height || bbox.height;
canvas.getContext('2d').drawImage(this, 0, 0, canvas.width, canvas.height);
// try to catch IE
try {
// if we set an <img> as receiver
if (receiver.nodeName === 'IMG') {
// make the img looks like the svg
receiver.setAttribute('style', getSVGStyles(receiver));
receiver.src = canvas.toDataURL(params, quality);
} else {
// make the canvas looks like the canvas
canvas.setAttribute('style', getSVGStyles(canvas));
// a container element
if (receiver.appendChild && receiver !== canvas)
receiver.appendChild(canvas);
// if we set a function
else if (typeof receiver === 'function')
receiver(canvas.toDataURL(params, quality), canvas);
}
} catch (ie) {
console.warn("Your ~browser~ has tainted the canvas.\n The canvas is returned");
if (receiver.nodeName === 'IMG') receiver.parentNode.replaceChild(canvas, receiver);
else receiver(null, canvas);
}
}
svgImg.onerror = function(e) {
if (svg._cleanedNS) {
console.error("Couldn't export svg, please check that the svgElement passed is a valid svg document.");
return;
}
// Some non-standard NameSpaces can cause this issues
// This will remove them all
function cleanNS(el) {
var attr = el.attributes;
for (var i = 0; i < attr.length; i++) {
if (attr[i].name.indexOf(':') > -1) el.removeAttribute(attr[i].name)
}
}
cleanNS(svg);
for (var i = 0; i < svg.children.length; i++)
cleanNS(svg.children[i]);
svg._cleanedNS = true;
// retry the export
exportDoc();
}
svgImg.src = svgURL;
}
// ToDo : find a way to get only usefull rules
var parseStyles = function() {
var styleS = [],i;
// transform the live StyleSheetList to an array to avoid endless loop
for (i = 0; i < document.styleSheets.length; i++)
styleS.push(document.styleSheets[i]);
// Do we have a `<defs>` element already ?
var defs = clone.querySelector('defs') || document.createElementNS('http://www.w3.org/2000/svg', 'defs');
if (!defs.parentNode)
clone.insertBefore(defs, clone.firstElementChild);
// iterate through all document's stylesheets
for (i = 0; i < styleS.length; i++) {
var style = document.createElement('style');
var rules = styleS[i].cssRules,
l = rules.length;
for (var j = 0; j < l; j++)
style.innerHTML += rules[j].cssText + '\n';
defs.appendChild(style);
}
// small hack to avoid border and margins being applied inside the <img>
var s = clone.style;
s.border = s.padding = s.margin = 0;
s.transform = 'initial';
}
var getSVGStyles = function(node) {
var dest = node.cloneNode(true);
svg.parentNode.insertBefore(dest, svg);
var dest_comp = getComputedStyle(dest);
var svg_comp = getComputedStyle(svg);
var mods = "";
for (var i = 0; i < svg_comp.length; i++) {
if (svg_comp[svg_comp[i]] !== dest_comp[svg_comp[i]])
mods += svg_comp[i] + ':' + svg_comp[svg_comp[i]] + ';';
}
svg.parentNode.removeChild(dest);
return mods;
}
var images = svg.querySelectorAll('image'),
total = images.length,
encoded = 0;
// Loop through all our <images> elements
for (var i = 0; i < images.length; i++) {
// check if the image is external
if (images[i].getAttributeNS(xlinkNS, 'href').indexOf('data:image') < 0)
toDataURL(images[i]);
// else increment our counter
else if (++encoded === total) exportDoc()
}
// if there were no <image> element
if (total === 0) exportDoc();
}
})
})
&#13;
#chartdiv1 {
width: 100%;
height: 500px;
}
.amcharts-export-menu {
display:none;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.amcharts.com/lib/3/amcharts.js"></script>
<script src="https://www.amcharts.com/lib/3/serial.js"></script>
<script src="https://www.amcharts.com/lib/3/themes/light.js"></script>
<script src="https://www.amcharts.com/lib/3/plugins/export/export.min.js"></script>
<script src="https://github.com/niklasvh/html2canvas/releases/download/0.5.0-alpha1/html2canvas.js"></script>
<button id="cmd">HTML2Canvas</button>
<div id="output">
<div id="chartdiv1">
</div>
</div>
&#13;
答案 2 :(得分:0)
在html2Canvas按钮上单击时,添加jQuery代码以将高度和宽度作为属性添加到svg元素(作为属性添加而不是样式添加),并且将以确切的格式导出
答案 3 :(得分:0)
我尝试了同样的方法,并通过更改SVG宽度和高度来解决此问题。这是用于解决此问题的代码。
function printOptions(type = 'before') {
var svg = chart.div.querySelector('svg');
if (svg) {
if (type == 'after') { // remove the attributes after generating canvas
svg.removeAttribute('width');
svg.removeAttribute('height');
} else { // set width and height according to parent container
svg.setAttribute('width', chart.div.clientWidth);
svg.setAttribute('height', chart.div.clientHeight);
}
chart.validateNow(); // validating am chart again.
}
}
// code for download image
downloadImage() {
printOptions();
html2canvas(document.getElementById('chart-div')).then(canvas => {
printOptions('after');
let a = document.createElement('a');
a.href = canvas.toDataURL("image/png")
a.download = 'Report.png';
a.click();
});
}
Working Demo ,您可以在将图像转换为导出的图像更改之前注释printOption。