简体   繁体   中英

Modify Canvas SVG Image with Javascript

I have a svg image that I can use to draw to canvas:

    image = new Image()
    image.src = "image.svg"

Is it possible to modify image to, for example, change the colour of an element?

Load the SVG

Instead of loading the SVG directly into an image element you can use the fetch function. Here a data URL, just for the example -- it can just be replaced by a URL. At this point you could store the SVG in a variable.

Parsing the SVG

To change the SVG one approach could be to use XPath . This is what happens in the function changeAttribute() that takes the SVG, an XPath expression and an object containing the data about what to change.

Load the SVG into an image element and render in canvas

As I read your question you already figured this out. In the function insertIntoCanvas() turn the SVG (XML document) into a data URL by using a FileReader . After that the image is created and drawn on the canvas.

 const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); fetch('data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTAgMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTSA1IDAgTCAwIDUgTCA1IDEwIEwgMTAgNSBaIiAvPgo8L3N2Zz4=').then(response => response.text()).then(text => { let xmlDoc = new DOMParser().parseFromString(text,'text/xml'); changeAttribute(xmlDoc, '//svg:path', {fill:'red'}); changeAttribute(xmlDoc, '//svg:svg', {width:100, height:100}); insertIntoCanvas(xmlDoc); }); function insertIntoCanvas(xmlDoc){ let file = new File([xmlDoc.rootElement.outerHTML], 'svg.svg', { type: "image/svg+xml" }); // and a reader let reader = new FileReader(); reader.addEventListener('load', e => { /* create a new image assign the result of the filereader to the image src */ let img = new Image(); // wait for it to got load img.addEventListener('load', e => { // update canvas with new image ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(e.target, 0, 0); }); img.src = e.target.result; }); // read the file as a data URL reader.readAsDataURL(file); } function changeAttribute(doc, xpath, obj) { let r = doc.evaluate(xpath, doc, nsResolver, XPathResult.ANY_TYPE, null); let nodes = []; let next = r.iterateNext(); while (next) { nodes.push(next); next = r.iterateNext(); } nodes.forEach(node => { Object.keys(obj).forEach(key => { node.setAttribute(key, obj[key]); }); }); } function nsResolver(prefix) { const ns = { 'xhtml': 'http://www.w3.org/1999/xhtml', 'svg': 'http://www.w3.org/2000/svg' }; return ns[prefix] || null; }
 canvas { border: thin solid black; }
 <canvas id="canvas" width="300" height="200"></canvas>

You would have to:

  • load the external SVG file as text
  • inject it in a shadowDOM (the browser will do the parsing)
  • add your <style> elements
  • extract the SVG outerHTML
  • replace invalid URI characters " and #
  • create a <canvas>
  • write SVG to canvas.img as XML DataURI
  • add your <canvas> to the shadowDOM
    or replaceWith the Web Component that did the work

A native JavaScript Web Component <svg-to-canvas> , supported in all modern browsers,
can it do all for you, so you only write:

<svg-to-canvas src="//load-file.github.io/heart.svg">
  <style>
    svg  { background:green }
    path { fill:red }
  </style>
</svg-to-canvas>

JSFiddle: https://jsfiddle.net/WebComponents/cosfrqyv/

StackOverflow snippet:

 <style> svg-to-canvas { background: lightgreen } canvas { background:pink } </style> <svg-to-canvas src="//load-file.github.io/heart.svg"> <style> svg { background:green } path { fill:red } </style> </svg-to-canvas> <svg-to-canvas replaceWith src="//load-file.github.io/heart.svg"></svg-to-canvas> <script> customElements.define("svg-to-canvas", class extends HTMLElement { async connectedCallback(svg,canvas,img) { this.style.display = "inline-block"; this.attachShadow({mode:"open"}).innerHTML = await (await fetch(this.getAttribute("src"))).text(); svg = this.shadowRoot.querySelector("svg"); svg.append(...this.querySelectorAll("*")); // stop here for an SVG inside shadowDOM canvas = document.createElement('canvas'); img = Object.assign(new Image(), { onload: () => { canvas.width = img.width; canvas.height = img.height; canvas.getContext('2d').drawImage(img, 0, 0); }, src: "data:image/svg+xml;utf8," + svg.outerHTML.replace(/"/g, "'").replace(/#/g, "%23") }); if (this.hasAttribute("replaceWith")) this.replaceWith(canvas); else { svg.remove(); this.shadowRoot.append(canvas) } } }) </script>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM