我想你正在处理一些svg到位图转换的边缘情况.
我不确定html2canvas使用哪种方法,但它肯定缺少这里的东西.
我编写了一个函数来处理一些在调用html2canvas之前可以使用的边缘情况,因为它能够毫无问题地绘制位图.
// without converting the svg to png
html2canvas(contentDiv, {
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(contentDiv, {
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 '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 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 `` 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
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 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 element
if (total === 0) exportDoc();
}
rect {
fill: blue;
transform: translate(35px) rotate(45deg);
}
div {
width: 250px;
display: inline-block
}
#svg {
border: 1px solid green;
}
#canvas { border: 1px solid red;}
Some html content
注意 :
从这个问题开始,我开始编写一个完整的exportInlineSVG函数,你可以找到here.