0

I want to use svg filter as kind of texture for one of my svg objects, but there is one problem (which I didn't realize while working in Illustrator, because it rasterizes filters). The problem is that the svg filter scales with the size of svg itself (responsive web page so we don't know final svg size). And instead of the desired texture we can get that or that etc

Is there some workaround? Maybe with css vars and calc() or native svg features? And should I use some "query" for HDPI displays to make it little bit bigger (x-times)?

Here is snippet with fixed svg size with correct display (should look like the picture "desired"):

#wrap {
height: 2320px;
width:  2744px;
}

svg {
width: 41%;
}
<div id="wrap">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1232 1232">
  <defs>
  <filter  height="100%" id="noise" width="100%" x="0%" y="0%">
    <feTurbulence  baseFrequency="0.75" numOctaves="5" stitchTiles="noStitch" type="fractalNoise"></feTurbulence>
    <feDiffuseLighting  lighting-color="#ccc" surfaceScale="17">
        <feDistantLight  azimuth="45" elevation="65"></feDistantLight>
    </feDiffuseLighting>
</filter>
    <style>
      .a {
        fill: #dedede;
        filter:url(#noise);
      }
    </style>
  </defs>
  <title>-stackoverex</title>
  <rect class="a" width="1232" height="1232"/>
</svg>
</div>

Let me try to clarify the situation. There is a responsive block, the right side of which is occupied by a svg image, at different screen resolutions (or a browser zoom) block (and the image) must be scaled to fit the user's viewport. The image is shown only on desktops starting from 992px and its initial size is about 655x440, it will increase somewhere up to 2620x1760 (if I'm not mistaken) on the PRO XDR display. A significant part of the svg is occupied by a wall with a "plaster" texture, which should look realistic enough and it is too expensive to use a bitmap and even with the maximum avif compression the full-size texture will be too heavy.

Update:

so we can see how the texture should change (or not change) with various sizes @Kaiido

Here is a snippet with the desired result (Hi-res embedded image used) A cleaned original svg was used containing the wall and header basic markup. You can go to full page view and play with it as you want.

Maybe I was doing something wrong, but I still couldn't get rid of the texture tiling at some resolutions with "original filter", besides, some devices did not display that filter in the most correct way. Also I can't find a way to scale down lighting filters for small resolutions (if at all possible).

In fact, as a workaround, we can exclude the lighting filters and use only the desaturated Turbulence. At resolutions less than 1400 or even more, it is visually quite similar and the filter itself is rendered much faster and is displayed correctly on all devices.

<filter id="stucco" height="100%" width="100%" x="0%" y="0%">
    <feTurbulence baseFrequency="0.9" xresult="colorNoise" stitchTiles="stitch" numOctaves="90" type="fractalNoise" />
    <feColorMatrix in="colorNoise" type="matrix" values=".33 .33 .33 0 0 .33 .33 .33 0 0 .33 .33 .33 0 0 0 0 0 1 0"/>
    <feComposite operator="in" in2="SourceGraphic" result="monoNoise"/>
    <feBlend in="SourceGraphic" in2="monoNoise" mode="multiply" />
</filter>

Not ideal, but acceptable, although there are some questions here too.

The first is that the filter lacks contrast (despite the "multiply" blending mode), the second is that for some reason media queries inside the svg styles do not work (although they should 1 2 etc) and it is impossible to adjust the baseFrequency for different resolutions and ratio> 1 (retina).

<svg>
  <defs>
    <style type="text/css">
        @media (-webkit-min-device-pixel-ratio: 1) and (min-width:: 992px)  {
            #SVG-noise {
              filter:url(#stucco-sm);
            }
         }
         @media (-webkit-min-device-pixel-ratio: 1) and (min-width:: 1920px)  {
            #SVG-noise {
              filter:url(#stucco-xl);
            }
         }
          @media (-webkit-min-device-pixel-ratio: 2) and (min-width:: 992px)  {
            #SVG-noise {
              filter:url(#stucco-hdpi);
            }
         }
7
  • 1
    Apologies, but it's not clear what you're trying to accomplish. Do you want the texture to scale when the graphic scales, or do you want it not to? For example, do you want a 10px x 10px section of the texture to look the same no matter what size the shape is? Light source filters are tricky because they are sometimes screen resolution dependent. Commented Jul 15, 2021 at 23:03
  • So you'd want the filter to be "fixed", as in your image? If so, why not produce a bitmap image from a defined size and then use that bitmap image as your texture? If you need it to be dynamically generated, then you could generate that bitmap from the page itself, e.g using a canvas element.
    – Kaiido
    Commented Jul 16, 2021 at 6:08
  • I have added clarification to the question
    – A-off
    Commented Jul 16, 2021 at 8:49
  • It's still unclear what you want... Could you make a mockup of different situations and how it should look? From what you say, I have the feeling that something as simple as this fiddle would be enough for your case: jsfiddle.net/d4r583n0
    – Kaiido
    Commented Jul 16, 2021 at 15:05
  • In a simplified form, svg looks like this
    – A-off
    Commented Jul 16, 2021 at 17:14

1 Answer 1

1

If the SVG has a viewBox, the contents will scale. If you don't want the scaling behaviour, remove the viewBox.

Here I've made the rectangle large (3000x3000) to cover a range of sizes. You can set the svg width and height to anything smaller than that and the filter pattern will remain constant. If you need something bigger than 3000x3000, then increase the size of the rectangle so the edges of it aren't visible.

svg {
width: 50%;
height: 300px;
}
<div id="wrap">
<svg>
  <defs>
  <filter  height="100%" id="noise" width="100%" x="0%" y="0%">
    <feTurbulence  baseFrequency="0.75" numOctaves="5" stitchTiles="noStitch" type="fractalNoise"></feTurbulence>
    <feDiffuseLighting  lighting-color="#ccc" surfaceScale="17">
        <feDistantLight  azimuth="45" elevation="65"></feDistantLight>
    </feDiffuseLighting>
</filter>
    <style>
      .a {
        fill: #dedede;
        filter:url(#noise);
      }
    </style>
  </defs>
  <title>-stackoverex</title>
  <rect class="a" width="3000" height="3000"/>
</svg>
</div>

3
  • It's quite unclear what they want exactly, but it seems pretty clear that they at least want the element on which this filter is applied to resize.
    – Kaiido
    Commented Jul 16, 2021 at 6:07
  • In theory, yes, but when I zoom the page svg changes its size. Please see my question (I have added some clarification of what I want to achieve).
    – A-off
    Commented Jul 16, 2021 at 8:51
  • I don't believe you mentioned zoom before :) You would need to dynamically adjust the filter using javascript every time the zoom changed. Unfortunately that's a bit tricky. There is no official way to get the current zoom (AFAIK). Although you can approximate it using methods such as this one. Commented Jul 17, 2021 at 0:26

Not the answer you're looking for? Browse other questions tagged or ask your own question.