Search code examples
javascriptsvgsvg-filters

Is there a way to use a SVG gradient as input to a displacement map?


I'm trying to generate a gradient that I could use as a feDisplacementMap to distort text in SVG. How do I need to set it?

I've tried creating it as part of the SVG, and hiding the gradient, but am unable to make it work this way.

  <svg
          filter="url(#f)"
          overflow="auto"
          viewBox="0,0,200vw,200vh"
          width="100%"
          height="100vh"
        >
          <defs>
            <radialGradient id="rg" r=".9">
              <stop offset="0%" stop-color="#f00"></stop>
              <stop offset="10%" stop-color="#000"></stop>
              <stop offset="20%" stop-color="#f00"></stop>
              <stop offset="30%" stop-color="#000"></stop>
              <stop offset="40%" stop-color="#f00"></stop>
              <stop offset="50%" stop-color="#000"></stop>
              <stop offset="60%" stop-color="#f00"></stop>
              <stop offset="70%" stop-color="#000"></stop>
              <stop offset="80%" stop-color="#f00"></stop>
              <stop offset="90%" stop-color="#000"></stop>
              <stop offset="100%" stop-color="#f00"></stop>
            </radialGradient>
    
            <filter id="f" primitiveUnits="objectBoundingBox">
              <feImage result="pict2" xlink:href="#witness"></feImage>
              <feDisplacementMap
                scale=".05"
                xChannelSelector="R"
                yChannelSelector="R"
                in2="pict2"
                in="SourceGraphic"
              ></feDisplacementMap>
            </filter>
    
            <pattern id="imageFill" width="1" height="1" viewBox="0 0 300 300">
              <image id="ripples" width="300" height="300" xlink:href="" />
            </pattern>
          </defs>
          <text height="100vh" text-anchor="middle" class="svgText">
            <tspan height="100vh" x="50%" y="50%">text</tspan>
          </text>
          <rect
            id="witness"
            width="100%"
            height="100%"
            stroke="none"
            opacity="0"
            fill="url(#rg)"
          />
        </svg>

I would like to be able to distort the text with the generated gradient while keeping it hidden. Thanks so much for your time and help :).


Solution

    • Radial gradients applied to text.

    .container {
    width:100%;
    height:100%;
    }
    #txt1 {
    font-size: 200px;
    font-weight:bold;
    font-family: 'Signika', sans-serif;
    fill:url(#rg);
    }
    <div class="container">
    <svg viewBox="0 0 750 300"    xmlns="http://www.w3.org/2000/svg"  
    xmlns:xlink="http://www.w3.org/1999/xlink">
                       
        <defs>
          <radialGradient id="rg" r=".9">
                  <stop offset="0%" stop-color="#f00"></stop>
                  <stop offset="10%" stop-color="#000"></stop>
                  <stop offset="20%" stop-color="#f00"></stop>
                  <stop offset="30%" stop-color="#000"></stop>
                  <stop offset="40%" stop-color="#f00"></stop>
                  <stop offset="50%" stop-color="#000"></stop>
                  <stop offset="60%" stop-color="#f00"></stop>
                  <stop offset="70%" stop-color="#000"></stop>
                  <stop offset="80%" stop-color="#f00"></stop>
                  <stop offset="90%" stop-color="#000"></stop>
                  <stop offset="100%" stop-color="#f00"></stop>
                </radialGradient>
        </defs>
        <text id="txt1" x="15%" y="75%"  >Stack</text>
      </svg>
      </div>

    • Add filters feTurbulence andfeDisplacementMap

    By changing the values of the baseFrequency andscale filter attributes you can get interesting effects

    Below is an example of a text distortion animation with gradients:

    The animation starts when you hover the text begin =" txt1.mouseover "

    the animation ends - end =" txt1.mouseout ", or at the end of the animation time - dur =" 18s "

    #txt1 {
    font-size: 200px;
    font-weight:bold;
    font-family: 'Signika', sans-serif;
    fill:url(#rg);
    filter:url(#myFilter);
    }
    <div class="container">
    <svg  width="750" height="300" version="1.1"
    viewBox="0 0 750 300"    xmlns="http://www.w3.org/2000/svg"  xmlns:xlink="http://www.w3.org/1999/xlink">
                       
        <defs>
           <radialGradient id="rg" r=".9">
                  <stop offset="0%" stop-color="#f00"></stop>
                  <stop offset="10%" stop-color="#000"></stop>
                  <stop offset="20%" stop-color="#f00"></stop>
                  <stop offset="30%" stop-color="#000"></stop>
                  <stop offset="40%" stop-color="#f00"></stop>
                  <stop offset="50%" stop-color="#000"></stop>
                  <stop offset="60%" stop-color="#f00"></stop>
                  <stop offset="70%" stop-color="#000"></stop>
                  <stop offset="80%" stop-color="#f00"></stop>
                  <stop offset="90%" stop-color="#000"></stop>
                  <stop offset="100%" stop-color="#f00"></stop>
                </radialGradient>
    	   <filter id="myFilter" >
             <feTurbulence type="turbulence" baseFrequency="0.0001" numOctaves="1" result="turbulence" >
              <animate attributeName="baseFrequency"
    		    dur="18s"
    			values="0.0001;0.02;0.0001;0.02;0.0001"
    			begin="txt1.mouseover"
    			end="txt1.mouseout" />
          </feTurbulence>
      	 <feDisplacementMap xChannelSelector="R" yChannelSelector="G" scale="25" in="SourceGraphic" in2="turbulence" />
        </filter>
        </defs>
        <text id="txt1" x="1%" y="50%"  >Stack</text>
    
    </div>

    • An example of animating image and text distortion

    .container {
    width:75%;
    height:75%;
    }
    
    #txt1 {
    font-size: 100px;
    font-weight:bold;
    font-family: 'Signika', sans-serif;
    fill:url(#rg);
    filter:url(#myFilter);
    pointer-events:none;
    }
    <div class="container">
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1"   viewBox="0 0 500 300">
      <defs> 
      <radialGradient id="rg" r=".9">
                  <stop offset="0%" stop-color="#f00"></stop>
                  <stop offset="10%" stop-color="#000"></stop>
                  <stop offset="20%" stop-color="#f00"></stop>
                  <stop offset="30%" stop-color="#000"></stop>
                  <stop offset="40%" stop-color="#f00"></stop>
                  <stop offset="50%" stop-color="#000"></stop>
                  <stop offset="60%" stop-color="#f00"></stop>
                  <stop offset="70%" stop-color="#000"></stop>
                  <stop offset="80%" stop-color="#f00"></stop>
                  <stop offset="90%" stop-color="#000"></stop>
                  <stop offset="100%" stop-color="#f00"></stop>
                </radialGradient>
        <filter id="myFilter" >
          <feTurbulence type="turbulence" baseFrequency="0.0001" numOctaves="1" result="turbulence" >
         <animate attributeName="baseFrequency" dur="18s" values="0.0001;0.02;0.0001;0.02;0.0001" begin="img1.mouseover" end="img1.mouseout" />
          </feTurbulence>
      	 <feDisplacementMap xChannelSelector="R" yChannelSelector="G" scale="25" in="SourceGraphic" in2="turbulence" />
        </filter>
      </defs>
    <image id="img1" xlink:href="https://i.sstatic.net/hHGO8.jpg" width="100%" height="100%" filter="url(#myFilter)" /> 
       <text id="txt1" x="35%" y="35%"  >Stack</text>
      </svg>
    </div>