SVG background-image blurriness in Chrome with repeating background

Viewed 2405

An SVG background is appearing Blurry in Chrome and other Blink/webkit based browsers:

Screenshot of a portion of the SVG in Chrome:

Chrome

Screenshot of a portion of the SVG in Firefox:

Firefox

This only happens when displaying the SVG with "background-repeat: true" and "background-size: contain" CSS values. We'd like to repeat the SVG background height as many times as is necessary, since the page height is dynamic and stretching the vector image would look poor.

The SVG is displayed inline in the CSS in the sample page below, but the issue happens in the same way when I link to the external SVG image.

Here's a test page which shows this behavior: https://august-13-2020-test-canvas.bubbleapps.io/version-test/pagemaker-test-original-2 (log in with "username" and "password", we're required to put this authentication here).

A test page with a properly displayed (crisp/smooth edged) SVG is here: https://august-13-2020-test-canvas.bubbleapps.io/version-test/pagemaker-test-online-1

A version with crisp/smooth edges is displayed in exactly the same way - the only difference is the content of the SVG.

Here's a link to the background SVG for the blurry page: https://dd7tel2830j4w.cloudfront.net/d44/f1588704991659x396570736162959360/background.svg

All the SVGs I tested from this page had crisp edges: https://www.svgbackgrounds.com/#flat-mountains (the bottom-right one is the one used in the above link)

In the process of trying to fix this issue or identify the cause of it within the SVG code, I've edited the SVG quite a bit, attempting the following:

  • Converting all SVG point values (path's d attributes) to integers
  • Adding "PreserveAspectRatio" to the SVG's opening tag
  • Removing the height and width from the opening declaration of the SVG
  • Removing the "linear gradients" from the SVG and replacing them with static colors
  • Doubling the size of the SVG shapes to see if the blurriness decreases with a larger initial width and height
  • Adding the "crispEdges" property to the SVG paths
  • Optimizing the SVG (using SVGomg)

Additionally, I've attempted to change the CSS a bit, but couldn't get it working.

Some ideas I have for workarounds are the following:

  • Using multiple background images to repeat the display of the SVG (display it 5-10 times in the background, mimicking the background-repeat: repeat-y effect but by including multiple background images)
  • Using JS to draw the SVG inline on the page (where this bug doesn't happen), creating an HTML that is exactly the same height as our group's dynamic height (which can change at any time without triggering a JS event) and repeating the SVG somehow within that group.
  • Asking a designer for a different SVG background, and hoping that the blurriness issue will not repeat.

Any help here would be much appreciated, I'm pulling my hair out on this and wasn't able to fix the issue after consulting several developers on CodeMentor.io.

2 Answers

Blink seems to apply a faulty algorithm, first rendering the SVG at a wrong size and then scaling the resulting raster image. This is tricky to work around, as two scaling mechanisms are involved, SVG viewBox and CSS background-size.

I can only offer a partial solution, working in Chrome and Edge (both engines), but failing in Firefox.

It moves the responsibility for scaling and repetition from the CSS background properties to the SVG itself with a <pattern> element. The complete content of the SVG image is moved inside this element, including setting the viewBox attribute to the same values.

Then, the width and height values for pattern repetition are set to width="100vw" height="504vw". This fills the viewport width and keeps the aspect ratio for the height. (Firefox seems not to be able to interpret vw units in this context).

Finally, a <rect> with width = height = 100% is defined and filled with the pattern, and the root <svg> element, without a viewBox attribute, is similarly set to width = height = 100%.

<svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg">
  <pattern id="pt" width="100vw" height="504vw" x="0" y="0" viewBox="0 0 1440 7258" patternUnits="userSpaceOnUse" preserveAspectRatio="xMinYMin meet">
    <rect width="1440" height="7258" fill="white"/>
    <path d="M-137 1749L1546 1994.95V2870L-137 2624.64V1749Z" fill="#F7F8FF"/>
    <path d="M-137 3776L1546 4021.95V4897L-137 4651.64V3776Z" fill="url(#paint0_linear)"/>
    <path d="M-137 3969L1546 4214.95V4900L-137 4654.64V3969Z" fill="url(#paint1_linear)"/>
    <path d="M1319 2764L1545 2796.96V2870L1319 2837.12V2764Z" fill="#264CE9"/>
    <path d="M-53 2645L96 2666.68V2744L-53 2722.37V2645Z" fill="#FBE89F"/>
    <path d="M-29 2571L307 2620.13V2699L-29 2649.99V2571Z" fill="#54A5F2"/>
    <path d="M1209 2820L1545 2869.13V2948L1209 2898.99V2820Z" fill="#6AD2F6"/>
    <path d="M-147 4649L307 4715.42V4891L-147 4824.74V4649Z" fill="#6AD2F6"/>
    <path d="M-137 -233L1546 12.9461V688L-137 442.638V-233Z" fill="url(#paint2_linear)"/>
    <g style="mix-blend-mode:multiply">
      <path d="M987 -144L2119 21.4151V388L987 222.978V-144Z" fill="#6AD2F6"/>
    </g>
    <path d="M-137 27L1546 272.946V948L-137 702.638V27Z" fill="url(#paint3_linear)"/>
    <path d="M-258 455L490 564.509V794L-258 684.751V455Z" fill="url(#paint4_linear)"/>
  </pattern>

  <linearGradient id="paint0_linear" x1="43.9999" y1="3807" x2="458.5" y2="4665" gradientUnits="userSpaceOnUse">
    <stop stop-color="#264CE9" stop-opacity="0.06"/>
    <stop offset="1" stop-color="#F7F8FF" stop-opacity="0.26"/>
  </linearGradient>
  <linearGradient id="paint1_linear" x1="43.9999" y1="4000" x2="1229" y2="5099" gradientUnits="userSpaceOnUse">
    <stop stop-color="#264CE9" stop-opacity="0.06"/>
    <stop offset="1" stop-color="#F7F8FF" stop-opacity="0"/>
  </linearGradient>
  <linearGradient id="paint2_linear" x1="194" y1="87.4999" x2="1671" y2="300" gradientUnits="userSpaceOnUse">
    <stop offset="0.270833" stop-color="#264CE9"/>
    <stop offset="1" stop-color="#6AD2F6"/>
  </linearGradient>
  <linearGradient id="paint3_linear" x1="194" y1="347.5" x2="1671" y2="560" gradientUnits="userSpaceOnUse">
    <stop stop-color="#264CE9"/>
    <stop offset="1" stop-color="#6AD2F6"/>
  </linearGradient>
  <linearGradient id="paint4_linear" x1="-108" y1="550.5" x2="490" y2="638" gradientUnits="userSpaceOnUse">
    <stop stop-color="#264CE9" stop-opacity="0.43"/>
    <stop offset="1" stop-color="#264CE9"/>
  </linearGradient>

  <rect width="100%" height="100%" fill="url(#pt)" />
</svg>

I have simplified the markup a bit, but these changes are all cosmetic, including the removal of the clipPath - it is applied for pattern elements, anyway.

Never found a solution to this issue (or at least failed to implement the solution presented by ccprog).

We chose to edit the SVG files to be longer. Our SVG files are being used as the background for web pages that have dynamic lengths, and theoretically have no limit in their height.

By repeating an SVG that is ~8000px tall 5 times, it covers up until a web page would reach 40000px height, which should be fine for our use case. I used inkscape to go in and copy the content of each SVG and then replicate that content several times, making the final SVGs 5x taller than the original ones.

Related