Home > Back-end >  Dynamic SVG injection in svelte
Dynamic SVG injection in svelte

Time:06-14

I'm rendering some dynamically generated SVGs in a svelte app that are generated as strings and then put into individual <svg> tags:

<script lang="ts">
  import { IconGenerator } from "../icons/iconGenerator";

  let icons = [];
  for (let index = 0; index < 15; index  ) {
    icons.push(IconGenerator.generate());
  }
</script>

<div>
  {#each icons as icon}
    <div>
      <svg viewBox="0 0 600 600" width="300" height="300">
        {@html icon}
      </svg>
    </div>
  {/each}
</div>

So far this works great, but I'd like to start using gradients which would be different per icon.

How can define the gradient's ids and keep them scoped only to the icon they belong with and not have them be applied to the whole page?

I've looked into a few SVG injector libraries that handle making unique ids but they all seem to be made for non dynamic svgs available at a url not made on the fly from strings.

CodePudding user response:

A version using Shadow DOM via use:action, where the ids wouldn't interfere REPL

<script>
    let icons = [
        `<defs><linearGradient id="gradient" gradientTransform="rotate(90)">
      <stop offset="5%"  stop-color="gold" />
        <stop offset="95%" stop-color="red" />
    </linearGradient></defs>
    <circle cx="100" cy="100" r="100" fill="url(#gradient)"/>`,

        `<defs><linearGradient id="gradient" x1="0" x2="0" y1="0" y2="1">
        <stop offset="0%" stop-color="teal"/>
        <stop offset="50%" stop-color="darkblue" stop-opacity="50"/>
        <stop offset="100%" stop-color="blue"/>
    </linearGradient></defs>
    <rect id="rect" width="200" height="200" rx="15" fill="url(#gradient)"/>`
    ]

    function shadow(node) {
        const shadowRoot = node.attachShadow({mode: 'open'})
        const children = Array.from(node.children)
        children.forEach(child => shadowRoot.appendChild(child))
    }
</script>

<div>
    {#each icons as icon, index}
    <div  use:shadow>
        <svg viewBox="-50 -50 300 300" width="200" height="200" style="display:block;">
            {@html icon}
        </svg>
    </div>
    {/each}
</div>

<style>
    .shadow-host {
        border: 2px solid lightgrey;
    }
</style>
  • Related