9am / 9am.github.io

9am Blog 🕘 ㍡
https://9am.github.io/
MIT License
3 stars 0 forks source link

Twist The Reality with feDisplacementMap #16

Open 9am opened 11 months ago

9am commented 11 months ago
joy hits
9am commented 11 months ago

Table of contents


Preface

Inspired by the famous album cover of Joy Division made by Peter Saville, I wanted to build an SVG filter that turns the source(image, text) into a bunch of 'contour' lines. That's where feDisplacementMap kicks in. It's one of my favorite filter primitives. In this article, I'll show you things we can do with it.

Unknown Pleasures Album cover by Peter Saville


𐄡


## Super power of `feDisplacementMap ` Most of the filter elements in SVG work like `Pixel shaders`, just do simple pixel in, pixel out. But `feDisplacementMap` offers the ability to re-arrange the position of pixels. > The SVG filter primitive uses the pixel values from the image from in2 to spatially displace the image from in.`` > > The formula for the transformation looks like this: > > P'(x,y) ← P(x + scale * (XC(x,y) - 0.5), y + scale * (YC(x,y) - 0.5)) >
> ![displacementmap](https://github.com/9am/9am.github.io/assets/1435457/6033ac14-ae20-44fb-8699-1540a99d4fa7) How the position displaces depends on the color channel of the `'Map'` input, yeah, we can control `source`'s pixel position by the color of the `Map`. Consider we have a green crossline as `source`, if we want to move the area in the center, we'll use the alpha channel, a `map` with `alpha != 1` rectangle placed in the center will do the job. The distance offset of the pixel could be controlled by `scale` and the value offset from '0.5' of the color channel. The direction is controlled by `xChannelSelector` or `yChannelSelector`. > how-works > [![Edit how-fedisplacementmap-works](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/how-fedisplacementmap-works-tmcqkj?fontsize=14&hidenavigation=1&theme=dark) With that, we'll build the contour filter to show the alpha value of the 'source', this is what we'll build. > ![final contour](https://github.com/9am/9am.github.io/assets/1435457/51250621-b1e4-48c7-b965-9ab33b8a48dd)

𐄡


## Contour filter
### 1. Create the stripes With the help of `` and ``, we can easily create a horizontal stripe pattern that fills the source target.

<div style="filter: url(#contour)"></div>
<svg>
  <defs>
    <polygon
      id="line"
      points="0,0 1000,0"
      fill="none"
      stroke="black"
      stroke-width="4px"
    />
    <filter
      id="contour"
      color-interpolation-filters="sRGB"
      x="0"
      y="0"
      width="100%"
      height="100%"
    >
      <feImage href="#line" width="1%" height="2%" />
      <feTile result="TILE" />
    </filter>
  </defs>
</svg>
    
s1

### 2. Prepare the map We have our `source`, which is the stripes. Now let's prepare the `map`. Add a `rgba(127, 127, 127, 0.5)` background to make an identity base map. Put some text into the `
`, make it `rgba(127, 256, 127, 1)`, and merge it with the stripes to debug. The alpha channel will be used to displace the stripe pixels.
<svg>
...
      <feMerge>
        <feMergeNode in="SourceGraphic" />
        <feMergeNode in="TILE" />
      </feMerge>
...
</svg>

<div><p>Twist The Reality !</p></div>
        
s2

### 3. Bend the lines Now using what we know about ``, move the pixel's `y` under the text up with `yChannalSelector='A'`, `x` stay the same since the `R` channel never changed. Remove the `map`, we can see the shape of the contour. But the stripes just shift position, not curved like a contour. Let's make it better by smoothing the alpha.
<svg>
...
      <feDisplacementMap
        in2="SourceGraphic"
        in="TILE"
        scale="10"
        xChannelSelector="R"
        yChannelSelector="A"
        result="OUTPUT"
      />
...
</svg>
        
s3 1 s3 2

### 4. Smooth the contour Apply a `` to make the alpha spread around, which can create a natural linear gradient for the text. The contour will be curved after this step. We can control the y-offset by increasing the `scale` of ``, and adjust the curve scope by increasing the `stdDeviation` of blur.
<svg>
...
    <filter
      id="contour"
      color-interpolation-filters="sRGB"
      x="0"
      y="0"
      width="100%"
      height="100%"
    >
    <feGaussianBlur
      stdDeviation=".8"
      result="BLUR"
    />
...
</svg>
        
s4 1 s4 2
> Throw more vertical stripes, and a `linear-gradient` to show alpha change. > > ![demo-1](https://github.com/9am/9am.github.io/assets/1435457/404d766e-3f51-4d38-ba61-3a98f71d6baf) > > [Codepen](https://codepen.io/9am/pen/dywaRZq)

𐄡


## Global Magnifier filter After the contour, I thought `` also could be used to make a Magnifier filter. Check it out: > ![demo-2](https://github.com/9am/9am.github.io/assets/1435457/95f521c0-c47c-40e9-ba29-aa0ac537e34b) > > [Codepen](https://codepen.io/9am/pen/eYbaQBw) I'll not explain the details, but here are some keys to take away: 1. Due to security reasons, a `` can not be used as `map` directly, one way is inline SVG directly into `href`. 2. Apply the filter with `backdrop-filter`, so the `SourceGraphic` will be anything under the target. In this way, the "Magnifier" can show everything on the web page. 3. Use `R` and `B` channel to create an "identity" map, then add a `grey` to `transparent` radial-gradient circle in the center which twist the edge of the circle. The type of Magnifier can be controlled by the `` position.

𐄡


## Closing thoughts There is so much fun playing with SVG filters, they're like built-in shaders that offer limited parameters, but with a little bit of innovation, they can be powerful and maybe it's the simplest way to tweak pixels on a web page. I'm considering writing a series to dig more for each of them. Thanks for reading, see you next time.
--- > ## @9am 🕘 > * Read more [articles](https://9am.github.io) at [9am.github.io](https://9am.github.io) > * Find other [things](https://www.npmjs.com/search?q=%409am) I built on [GitHub](https://github.com/9am) and [NPM](https://www.npmjs.com/~9am) > * Contact me via [email](mailto:tech.9am@gmail.com) > * [![Creative Commons License](https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png)](http://creativecommons.org/licenses/by-nc-nd/4.0/)