Search code examples
javascripthtmlsvgsvg-filters

Use the same SVG filter with different seeds or offsets


In my html page, I have an SVG filter like this:

<filter id="displacementFilter" height="180%" width="180%">
    <feTurbulence
      type="turbulence"
      baseFrequency="0.05"
      numOctaves="2"
      result="turbulence" />
    <feDisplacementMap
      in2="turbulence"
      in="SourceGraphic"
      scale="50"
      xChannelSelector="R"
      yChannelSelector="G" />
  </filter>

In order to make the filtered elements look different, I want to use this same filter with different seeds or offsets for the <feTurbulence> tag. I don't want to repeat the filter several times since this will significantly increase the size of the page and because the number of elements that use the filter is variable between pages. Is there a solution?


Solution

  • Use a native JavaScript Web Component (JSWC), supported for nearly a decade now.

    The shadowDOM makes sure you don't run into duplicate IDs trouble.
    If you do not want shadowDOM, you have to create unique IDs with crypto.randomUUID()

    <h1 style="font:18px arial">Who is afraid of Red, Yellow and Blue Web Components?</h1>
    <script>
      customElements.define('svg-splash', class extends HTMLElement {
        connectedCallback() {
          let color     = this.getAttribute('color')     || 'red';
          let frequency = this.getAttribute('frequency') || 0.05;
          this.attachShadow({ mode: 'open' }).innerHTML = `
          <svg style="background:grey" width="140" viewBox="0 0 120 120">
            <filter id="displacementFilter" height="180%" width="180%">
              <feTurbulence type="turbulence" numOctaves="2" 
                            baseFrequency="${frequency}" result="turbulence" />
              <feDisplacementMap in2="turbulence" in="SourceGraphic" scale="50"
                                 xChannelSelector="R" yChannelSelector="G" />
            </filter>
            <circle cx="50" cy="50" r="40" fill="${color}" 
                    filter="url(#displacementFilter)" />
          </svg>`;
          }
      });
    </script>
    
    <svg-splash frequency="0.05"></svg-splash>
    <svg-splash frequency="0.1" color="yellow"></svg-splash>
    <svg-splash frequency="0.5" color="blue"></svg-splash>

    https://en.wikipedia.org/wiki/Who's_Afraid_of_Red,_Yellow_and_Blue

    Dive deeper into Web Components, and you can make frequency and color attributes reactive with "ObservedAttributes", updating the splash on every attribute change.
    Because that is what attributes in HTML do.

    Unlike Frameworks it does not matter when you define the Web Component.
    Any existing (but undefined) <svg-splash> elements in the page are automagically upgraded by the Browser.