问题
I have this website: http://materialground.com/icon-maker
I have this code above my body
<symbol id="fa-flaticon-3" viewBox="0 0 512 512">
<path d="m512 247c0 118-87 216-200 233 1-5 2-11 1-16 0 0-1-6-2-14 41-6 77-25 105-51-1-2-3-3-5-6-7-11 3-22 3-32 0-9-20-9-20-15-3-22 13-22 22-30 8-9-9-28-20-27-11 1-41-5-39-28 4-29-36-27-42-40-8-19 6-43 24-49 25-7 51-28 49-52-3-26-14-50-34-64-21-8-43-13-66-14-20 0-38 5-37 14 2 23 67 29 56 51-6 11-49 27-41 44 6 11 19 1 14 22-2 10-11 28-24 29-13 1-24-34-60-35-17 0-41 27-21 46 12 12 36-14 42 6 5 14 0 44 22 56 9 5 21 9 34 15-19 4-42 11-68 21l-23-6c10-8 25-13 21-30-6-26-50-16-80-46-9-10-29-40-29-77-15 28-23 61-23 95 0 11 0 21 2 32 0 0-1 0-1 0-10 0-19 2-28 5-1-12-2-24-2-37 0-129 105-235 235-235 129 0 235 106 235 235z m-176 69l-58 26 1-12 37-17c-5 0-11-1-16-1-21 0-55 11-94 26l-123-32c-12-3-24-2-34 5l-6 4c-6 3-6 12 0 16l74 48c-18 9-35 18-49 27l-38-20c-7-4-14-4-20-1l-4 1c-5 2-8 9-5 14l21 37 0 0c-8 7-12 12-12 16 0 12 14 15 27 15 22 0 321-69 321-130 0-13-9-19-22-22z m-126 121l65-23c3-1 6 1 7 4l6 49c1 12-6 24-17 29l-6 3c-4 2-8 1-11-2l-46-53c-2-2-1-6 2-7z"></path>
</symbol>
And below i have this code :
<svg viewBox="0 0 256 512" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1" class="fa-youtube red draggable" vbox="0 0 256 512" id="img" width="341.3333333333333" fill="#fff" height="512" style="width:512 !important; height:512 !important; padding:0; border-radius:10%">
<use xlink:href="#fa-flaticon"></use>
</svg>
I want the root svg tag to be modified by users like fill color. background color, stroke, viewbox etc.
Now the question is how to save the svg to png using canvg or any other script. I can also use php script.
I have used this code but its not working
function renderCanvas()
{
var oSerializer = new XMLSerializer();
var sXML = oSerializer.serializeToString(document.getElementById("svg"));
canvg(document.getElementById('canvas'), sXML,{ ignoreMouse: true, ignoreAnimation: true })
}
I also added blank canvas.
<canvas id="canvas"></canvas>
回答1:
Most of the svg to canvas libraries will fail on external resources (images, uses, symbols and any other attribute in the xlink
name-space or with a <funciri>
(url()
) into their attribute.
I am writing a script that does handle those cases.
It does search for such external resources, and append it to a clone of the svg element to be converted, and then uses the canvas drawImage()
ability to render svg.
Its usage is quite simple, and may be even simpler in a near future.
SVG2bitmap(SVGElement, [function([canvasElement],[dataURL]) || IMGElement || CanvasElement] [, Object{optional parameters}])
Here is a dump of the function that do parse elements with such external attributes :
function parseXlinks() {
// some browsers don't support the asterisk namespace selector (guess which ones ...)
// create a test element
var test = document.createElementNS('http://www.w3.org/2000/svg', 'use');
// set its href attribute to something that should be found
test.setAttributeNS(xlinkNS, 'href', '__#__');
// append it to our document
clone.appendChild(test);
// if querySelector returns null then the selector is not supported
var supported = !!clone.querySelector('[*|href*="#"]'),
xl,
i;
// the test is done, remove the element
clone.removeChild(test);
// if the selector is not supported
if (!supported) {
// xl is an array
xl = [];
// iterate through all our elements
var children = clone.querySelectorAll('*');
for (i = 0; i < children.length; i++) {
// search the xlink:href attribute
var xl_attr = children[i].getAttributeNS(xlinkNS, 'href');
// we only want the ones that refer to elements
if (xl_attr && xl_attr.indexOf('#') > -1) {
xl.push(children[i]);
}
}
} else {
// get all our elements using an xlink:href attribute
xl = clone.querySelectorAll('[*|href*="#"]');
}
// the list of all attributes that can have a <funciri> (url()) as value
var url_attrs = ["clip-path", "src", "cursor", "fill", "filter", "marker", "marker-start", "marker-mid", "marker-end", "mask", "stroke"];
// build our selector string
var urlSelector = '[*|' + url_attrs.join('*="url"], *[*|') + '*="url"]';
// query is magic
var url = clone.querySelectorAll(urlSelector);
// we found something
if (xl.length || url.length) {
// create a <defs> or get the svg's one
getDef();
}
// there is no such elements ?
else {
// continue directly with images
parseImages();
return;
}
// create an array for external docs
var externals = [],
inDoc = [];
var getElements = function(arr) {
for (var i = 0; i < inDoc.length; i++) {
var el = inDoc[i];
// not in our actual svg ?
if (!svg.getElementById(el)) {
// get it somewhere else in the page
var ref = document.querySelector('#' + el);
// failed
if (!ref) {
console.warn('could not find this element : ', '#' + el);
continue;
}
// we got it, add a clone to our svg
defs.appendChild(ref.cloneNode(true));
}
}
};
// fetch the external documents
var addFile = function(url) {
var pushed = false;
for (var i = 0; i < externals.length; i++) {
// if we already have this document, just push the element
if (url[0] === externals[i].file_url) {
pushed = true;
externals[i].elements.push(url[1]);
checkParse();
}
}
// that was a new doc
if (!pushed) {
// create the object we'll use for this doc
var that = {
file_url: url[0],
elements: [url[1]],
loading: null
};
// add it to our array
externals.push(that);
// create a new request
var xhr = new XMLHttpRequest();
xhr.onload = function() {
// we're not loading anymore
that.loading = false;
// everything went fine
if (this.status === 200) {
that.response = this.responseText;
} else {
console.warn('could not load this document :', url[0], '\n' +
'Those elements are lost : ', that.elements.join(' , '));
}
// In case we were the last one
checkParse();
};
xhr.onerror = function(e) {
that.loading = false;
console.warn('could not load this document', url[0]);
console.warn('Those elements are lost : ', that.elements.join(' , '));
checkParse();
};
xhr.open('GET', that.file_url);
that.loading = true;
xhr.send();
}
};
var checkParse = function() {
// there are still pending loadings
if (externals.some(function(o) {
return o.loading;
})) {
return;
} else {
// loop through all our documents
for (var i = 0; i < externals.length; i++) {
// if we failed to load it
if (!externals[i].response) {
continue;
}
// create a new doc from the response
var doc = new DOMParser().parseFromString(externals[i].response, 'image/svg+xml');
// loop through the elements we use in this document
var els = externals[i].elements;
for (var j = 0; j < els.length; j++) {
// get the new id we'll use in our svg file
var newId = externals[i].file_url.replace('.svg', '_') + els[j];
// this one was already appended
if (defs.querySelector('#' + newId)) {
continue;
}
// find it in the response doc
var elem = doc.documentElement.querySelector('#' + els[j]);
if (elem) {
// we found it
var clone = elem.cloneNode(true);
clone.setAttribute('id', newId);
defs.appendChild(clone);
} else {
console.warn('could not find this element : ', externals[i].file_url + '#' + els[j]);
}
}
}
// all responses have been parsed
// we can continue with images
parseImages();
}
};
// get the attributes containing the <funciri>
for (i = 0; i < url.length; i++) {
// get all our node's attributes
var att = url[i].attributes;
// store a new array to our node
url[i].external_attr = [];
for (var j = 0; j < att.length; j++) {
// does it have a <funciri> ?
if (att[j].value.indexOf('url(') > -1) {
// add it to the array
url[i].external_attr.push(att[j]);
}
}
}
var split_attr = function(list, type) {
// loop through our list to get the external elements
for (var i = 0; i < list.length; i++) {
var hrefs = [],
j;
if (type === 'xlink') {
// get the href attribute of our element
hrefs.push(list[i].getAttributeNS(xlinkNS, 'href').split('#'));
} else {
// loop through all attributes containing a <funciri>
var attr = list[i].external_attr;
for (j = 0; j < attr.length; j++)
hrefs.push(attr[j].value.substring(4).slice(0, -1).split('#'));
}
for (j = 0; j < hrefs.length; j++) {
var href = hrefs[j];
// it does point to an external doc
if (href[0].indexOf('.svg') > 0) {
addFile(href);
// a new id if different external docs uses the same ids
var newId = '#' + href[0].replace('.svg', '_') + href[1];
// 'xlink' case
if (type === 'xlink')
list[i].setAttributeNS(xlinkNS, 'href', newId);
// <funciri> case
else
list[i].setAttribute(list[i].external_attr[j].name, 'url(' + newId + ')');
}
// it should be inside the page
else if (!href[0]) {
// push it to our array if it's not there already
if (inDoc.indexOf(href[1] < 0)) {
inDoc.push(href[1]);
}
}
}
}
if (inDoc.length) {
getElements(inDoc);
}
};
split_attr(xl, 'xlink');
split_attr(url, 'funciri');
// all was done synchronously or before we finished parsing (not sure this can happen)
if (externals.length === 0 || !externals.some(function(o) {
return o.loading;
})) {
exportDoc();
}
}
Live example :
var svg = toPixel;
var clone = svg.cloneNode(true);
var doSomethingWith = function(canvas) {
document.body.appendChild(canvas)
};
var xlinkNS = 'http://www.w3.org/1999/xlink';
function parseXlinks() {
// some browsers don't support the asterisk namespace selector (guess which ones ...)
// create a test element
var test = document.createElementNS('http://www.w3.org/2000/svg', 'use');
// set its href attribute to something that should be found
test.setAttributeNS(xlinkNS, 'href', '__#__');
// append it to our document
clone.appendChild(test);
// if querySelector returns null then the selector is not supported
var supported = !!clone.querySelector('[*|href*="#"]'),
xl,
i;
// the test is done, remove the element
clone.removeChild(test);
// if the selector is not supported
if (!supported) {
// xl is an array
xl = [];
// iterate through all our elements
var children = clone.querySelectorAll('*');
for (i = 0; i < children.length; i++) {
// search the xlink:href attribute
var xl_attr = children[i].getAttributeNS(xlinkNS, 'href');
// we only want the ones that refer to elements
if (xl_attr && xl_attr.indexOf('#') > -1) {
xl.push(children[i]);
}
}
} else {
// get all our elements using an xlink:href attribute
xl = clone.querySelectorAll('[*|href*="#"]');
}
// the list of all attributes that can have a <funciri> (url()) as value
var url_attrs = ["clip-path", "src", "cursor", "fill", "filter", "marker", "marker-start", "marker-mid", "marker-end", "mask", "stroke"];
// build our selector string
var urlSelector = '[*|' + url_attrs.join('*="url"], *[*|') + '*="url"]';
// query is magic
var url = clone.querySelectorAll(urlSelector);
// we found something
if (xl.length || url.length) {
// create a <defs> or get the svg's one
getDef();
}
// there is no such elements ?
else {
// continue directly with images
parseImages();
return;
}
// create an array for external docs
var externals = [],
inDoc = [];
var getElements = function(arr) {
for (var i = 0; i < inDoc.length; i++) {
var el = inDoc[i];
// not in our actual svg ?
if (!svg.getElementById(el)) {
// get it somewhere else in the page
var ref = document.querySelector('#' + el);
// failed
if (!ref) {
console.warn('could not find this element : ', '#' + el);
continue;
}
// we got it, add a clone to our svg
defs.appendChild(ref.cloneNode(true));
}
}
};
// fetch the external documents
var addFile = function(url) {
var pushed = false;
for (var i = 0; i < externals.length; i++) {
// if we already have this document, just push the element
if (url[0] === externals[i].file_url) {
pushed = true;
externals[i].elements.push(url[1]);
checkParse();
}
}
// that was a new doc
if (!pushed) {
// create the object we'll use for this doc
var that = {
file_url: url[0],
elements: [url[1]],
loading: null
};
// add it to our array
externals.push(that);
// create a new request
var xhr = new XMLHttpRequest();
xhr.onload = function() {
// we're not loading anymore
that.loading = false;
// everything went fine
if (this.status === 200) {
that.response = this.responseText;
} else {
console.warn('could not load this document :', url[0], '\n' +
'Those elements are lost : ', that.elements.join(' , '));
}
// In case we were the last one
checkParse();
};
xhr.onerror = function(e) {
that.loading = false;
console.warn('could not load this document', url[0]);
console.warn('Those elements are lost : ', that.elements.join(' , '));
checkParse();
};
xhr.open('GET', that.file_url);
that.loading = true;
xhr.send();
}
};
var checkParse = function() {
// there are still pending loadings
if (externals.some(function(o) {
return o.loading;
})) {
return;
} else {
// loop through all our documents
for (var i = 0; i < externals.length; i++) {
// if we failed to load it
if (!externals[i].response) {
continue;
}
// create a new doc from the response
var doc = new DOMParser().parseFromString(externals[i].response, 'image/svg+xml');
// loop through the elements we use in this document
var els = externals[i].elements;
for (var j = 0; j < els.length; j++) {
// get the new id we'll use in our svg file
var newId = externals[i].file_url.replace('.svg', '_') + els[j];
// this one was already appended
if (defs.querySelector('#' + newId)) {
continue;
}
// find it in the response doc
var elem = doc.documentElement.querySelector('#' + els[j]);
if (elem) {
// we found it
var clone = elem.cloneNode(true);
clone.setAttribute('id', newId);
defs.appendChild(clone);
} else {
console.warn('could not find this element : ', externals[i].file_url + '#' + els[j]);
}
}
}
// all responses have been parsed
// we can continue with images
parseImages();
}
};
// get the attributes containing the <funciri>
for (i = 0; i < url.length; i++) {
// get all our node's attributes
var att = url[i].attributes;
// store a new array to our node
url[i].external_attr = [];
for (var j = 0; j < att.length; j++) {
// does it have a <funciri> ?
if (att[j].value.indexOf('url(') > -1) {
// add it to the array
url[i].external_attr.push(att[j]);
}
}
}
var split_attr = function(list, type) {
// loop through our list to get the external elements
for (var i = 0; i < list.length; i++) {
var hrefs = [],
j;
if (type === 'xlink') {
// get the href attribute of our element
hrefs.push(list[i].getAttributeNS(xlinkNS, 'href').split('#'));
} else {
// loop through all attributes containing a <funciri>
var attr = list[i].external_attr;
for (j = 0; j < attr.length; j++)
hrefs.push(attr[j].value.substring(4).slice(0, -1).split('#'));
}
for (j = 0; j < hrefs.length; j++) {
var href = hrefs[j];
// it does point to an external doc
if (href[0].indexOf('.svg') > 0) {
addFile(href);
// a new id if different external docs uses the same ids
var newId = '#' + href[0].replace('.svg', '_') + href[1];
// 'xlink' case
if (type === 'xlink')
list[i].setAttributeNS(xlinkNS, 'href', newId);
// <funciri> case
else
list[i].setAttribute(list[i].external_attr[j].name, 'url(' + newId + ')');
}
// it should be inside the page
else if (!href[0]) {
// push it to our array if it's not there already
if (inDoc.indexOf(href[1] < 0)) {
inDoc.push(href[1]);
}
}
}
}
if (inDoc.length) {
getElements(inDoc);
}
};
split_attr(xl, 'xlink');
split_attr(url, 'funciri');
// all was done synchronously or before we finished parsing (not sure this can happen)
if (externals.length === 0 || !externals.some(function(o) {
return o.loading;
})) {
exportDoc();
}
}
var defs;
var getDef = function() {
// Do we have a `<defs>` element already ?
defs = svg.querySelector('defs') || document.createElementNS('http://www.w3.org/2000/svg', 'defs');
if (!defs.parentNode) {
svg.insertBefore(defs, svg.firstElementChild);
}
};
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.getBoundingClientRect();
if (svg.width.baseVal.unitType !== 1) svg.setAttribute('width', bbox.width);
if (svg.height.baseVal.unitType !== 1) svg.setAttribute('height', bbox.height);
// serialize our node
var svgData = (new XMLSerializer()).serializeToString(svg);
// remember to encode special chars
var svgURL = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData);
var svgImg = new Image();
svgImg.onload = function() {
var canvas = 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(svgImg, 0, 0, canvas.width, canvas.height);
doSomethingWith(canvas)
};
svgImg.src = svgURL;
};
parseXlinks();
canvas {
border: 1px solid green !important;
}
<script src="https://rawgit.com/Kaiido/SVG2Bitmap/master/SVG2Bitmap.js"></script>
<svg style="display: none">
<symbol id="sym01" viewBox="0 0 150 110">
<circle cx="50" cy="50" r="40" stroke-width="8" stroke="red" fill="red" />
<circle cx="90" cy="60" r="40" stroke-width="8" stroke="green" fill="white" />
</symbol>
</svg>
<svg id="toPixel">
<use xlink:href="#sym01" />
</svg>
<br>canvas version:
<br>
or using the entire script :
SVG2bitmap(toPixel, function(canvas){
document.body.appendChild(canvas);
});
canvas { border: 1px solid green !important;}
<script src="https://rawgit.com/Kaiido/SVG2Bitmap/master/SVG2Bitmap.js"></script>
<svg style="display: none">
<symbol id="sym01" viewBox="0 0 150 110">
<circle cx="50" cy="50" r="40" stroke-width="8" stroke="red" fill="red" />
<circle cx="90" cy="60" r="40" stroke-width="8" stroke="green" fill="white" />
</symbol>
</svg>
<svg id="toPixel">
<use xlink:href="#sym01" />
</svg>
<br>canvas version:
<br>
来源:https://stackoverflow.com/questions/34094256/convert-inline-svg-to-canvas-using-canvg-script