Convert Inline svg to canvas using canvg script

送分小仙女□ 提交于 2020-01-04 05:10:16

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!