Use svgloader to create html svg element?

Just a quick question, is it possible to take the data you got from using svgLoader on an imported svg and put it back into an svg element?

I want to be able to edit the colors and size of the svg before converting it into a png. The png will be used as the texture, instead of breaking the svg down into many small meshes, which makes my app lag.

Thanks!

If you’re converting back to SVG, is there still a need for using the SVGLoader?

Since there is an SVG DOM, you could manipulate the SVG directly before converting to PNG.

If I’m misunderstanding, can you provide some more details

Because according to this stackoverflow post, if the svg is just a pathname of an external file, it is not able to be edited. The svgs I am using are not embedded.

Internally, SVGLoader loads the SVG as an XML dom.

Knowing, this, it might be easier to query the XML DOM for the parts you need to manipulate.

See the parseNode method for how to pick out the parts you’re interested in.

1 Like

THREE.SVGLoader unpacks an SVG into three.js objects compatible with THREE.Mesh and THREE.BufferGeometry. THREE.SVGRenderer draws a three.js object or scene as an SVG.

If your end goal is to edit an SVG and then convert it to a texture … then I don’t think either is the right tool for that. Edit the SVG directly or use libraries like D3.js, and then draw the SVG to canvas. Editing the SVG via a THREE.BufferGeometry object will be much harder.

Note that SVGLoader is not a full implementation of SVG and has many limitations - gradients and text to name a couple.

If you know ahead of time that all the SVG files load and render correctly that’s probably worth checking too before investing more time in SVGLoader for your needs.

They do render correctly, but just in case if a user wanted to import their own svgs into the app and their svgs had text or gradients, I would probably not use SVGLoader then.

That sounds helpful, I was about to use D3 js for a different part of the app.

Is querying the XML DOM possible in React? Maybe in a ref it could be. My app uses React, sorry for not specifying that before.

I found this post that would use D3 and then select the DOM element.

I am guessing that upon selecting the element I could edit the colors and size of the DOM element before drawing the svg to a canvas.

I could then take the canvas image and pass it to each 3D object.

You should be able to use the standard XML document methods to query the SVG for specific attributes. d3 may not be needed. For example,

   const xmlDoc = svg.documentElement
   let fills = xmlDoc.querySelectorAll(`[fill]`);
   fills.forEach(element => {
     if (element.getAttribute('fill') == '#eee')
       element.setAttribute('fill', 'blue');
   })

This returns all the elements that have a fill and changes a specific match to blue.

2 Likes

Thank you all for your patience and time.

Do I first need to fetch the image, based on the answer from this [post]?(Load svg from file using JavaScript - Stack Overflow)

@anidivr @donmccurdy It turns out I have no idea what I am doing in terms of actually fetching the svg and querying it and thought I could try and figure it out by myself, but I need some more help.

Here is what I have so far:

const defaultSymbolFiles = import.meta.glob('/public/presets_library/symbol_presets/*.svg')
const defaultSymbolFileNames = Object.keys(defaultSymbolFiles)

const defaultSymbolColors = [
  'darkorchid',
  'magenta',
  'darkred',
  '#ff5b5b',
  'darkorange',
  'darkgoldenrod',
  '#13be00',
  'darkgreen',
  'darkcyan',
  'darkturquoise',
  'cornflowerblue',
  'darkblue',
  '#641d6d',
  'saddlebrown',
  'black',
  'dimgray',
]

const combosArr = [...defaultSymbolFileNames].map((file, index) => {
  return {
    symbol: file,
    color: defaultSymbolColors[index]
  }
})

for (let symbolComboIndex = 0; symbolComboIndex < combosArr; symbolComboIndex++) {
  const xml = await new Promise((resolve, reject) => {
    // fetching the symbol using the path
    fetch(combosArr[symbolComboIndex]['symbol'])
    .then(data => {
      resolve(data)
    })
  })
  .then(data => svgRef.current.innerHTML = data)
  .then(data => new DOMParser().parseFromString( data, 'image/svg+xml'))
  .then(body => body.text())
}

If you know what I might be doing wrong, please let me know.

Good news! I’ve discovered by using Three.js’s THREE.FileLoader, I get just the html bits while not having it convert to an object consisting of all the paths. Before I have tried using fetch to get it, but it was taking too long and the symbols for the tiles were not readily available.