javascript - 使用 canvg 脚本将内联 svg 转换为 Canvas

标签 javascript php canvas svg canvg

我有这个网站:http://materialground.com/icon-maker

我身上有这段代码

<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>

下面我有这段代码:

<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>

我想让用户修改根 svg 标签,比如填充颜色。背景颜色、描边、 View 框等

现在的问题是如何使用 canvg 或任何其他脚本将 svg 保存为 png。我也可以使用 php 脚本。

我用过这段代码,但它不起作用

function renderCanvas()
{
    var oSerializer = new XMLSerializer();
    var sXML = oSerializer.serializeToString(document.getElementById("svg"));

    canvg(document.getElementById('canvas'), sXML,{ ignoreMouse: true, ignoreAnimation: true })
}

我还添加了空白 Canvas 。

<canvas id="canvas"></canvas>

最佳答案

大多数 svg 到 Canvas 库将在外部资源(图像、用途、符号和 xlink 命名空间中的任何其他属性或带有 <funciri>(url())到其属性中的任何其他属性上失败。

我正在写 a script确实处理了这些情况。

它确实会搜索此类外部资源,并将其附加到要转换的 svg 元素的克隆中,然后使用 Canvas drawImage()渲染 svg 的能力。

它的用法非常简单,在不久的将来可能会更简单。

SVG2bitmap(SVGElement, [function([canvasElement],[dataURL]) || IMGElement || CanvasElement] [, Object{optional parameters}])

这是解析具有此类外部属性的元素的函数的转储:

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 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>

或使用整个脚本:

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>

关于javascript - 使用 canvg 脚本将内联 svg 转换为 Canvas ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34094256/

相关文章:

javascript - 如何从另一个对象调用一个对象的方法?

javascript - 使用 angular 指令绘制 highchart 饼图但是当我在成功函数中使用它时它不起作用

javascript - 正则表达式 x 之后直到空格的所有内容

javascript - Bootstrap 折叠菜单不显示谷歌地图

php - 使用 php mysqli 从两个连接的表中获取数据

php - 将用户的输入存储到数据库中

PHP 只在数组中存储 1048576 个字符

javascript - 如何使用 javascript 更改 Canvas 中动画的颜色?

canvas - 在对象缩放时,fabric.js 中矩形的宽度和高度不会改变

javascript - 如何构建: 3D Canvas objects and manipulation - tylko. com