Home > Back-end >  SVG - Display fill before image loads
SVG - Display fill before image loads

Time:05-02

I currently have a working example, but I'm wondering if there's someone out there with more knowledge on SVGs than myself who can compress the code and make it more elegant.

What I'm trying to do:

  1. Show image inside the custom defined path (shape) once page is loaded ("lazy load")
  2. Until page load (on slower devices most importantly) show a fill as background.

See the fiddle and code here:

https://jsfiddle.net/k2o9L6dg/

Replicate the issue

All is working fine as you can see, but I would like to remove the <g> tag. If I remove this tag, the shape is transparent/white until the image loads. You can throttle under the "Network" tab to see this user experience. I would like to use a grey fill until image loads, so text can be displayed within the shape.

I feel like it should be possible to make this work, without having to define the "path" two times. Whenever I change the fill on the mask path to other than white, nothing appears at all (no idea why this happens).

I would like to be able to compress the code to just this:

<div >
    <svg xmlns="http://www.w3.org/2000/svg" image-rendering="optimizeQuality" viewBox="0 0 510 360" shape-rendering="geometricPrecision" fill-rule="evenodd">
                                
        <defs>
            <mask id="bg-c" maskContentUnits="objectBoundingBox">
                <path fill="white" transform="scale(0.001965, 0.002800)" d="M148.500 3.099 C 113.488 6.747,84.700 13.892,62.351 24.482 C 43.108 33.600,33.681 41.189,22.444 56.609 C 7.759 76.760,4.338 86.781,5.282 106.890 C 5.880 119.655,7.968 128.762,13.637 143.340 C 23.834 169.561,23.443 167.883,23.464 185.500 C 23.479 197.898,23.041 203.414,21.520 210.000 C 18.603 222.635,18.745 240.097,21.847 249.975 C 30.657 278.033,52.991 299.700,93.500 319.490 C 109.905 327.505,121.171 331.756,142.440 337.958 C 190.118 351.861,258.762 358.886,289.318 352.989 C 307.253 349.528,331.710 340.925,364.262 326.626 C 392.030 314.428,408.965 308.298,425.480 304.468 C 451.051 298.538,471.322 283.403,481.509 262.633 C 487.363 250.696,489.054 243.962,489.701 230.000 C 490.547 211.754,486.958 197.061,477.358 179.483 C 469.662 165.389,471.689 154.395,491.588 102.309 C 506.590 63.041,509.743 48.582,505.331 39.285 C 501.149 30.471,482.609 22.301,459.167 18.940 C 445.334 16.957,405.463 18.286,371.500 21.862 C 319.125 27.376,299.077 27.919,277.500 24.407 C 270.080 23.199,265.779 21.647,253.000 15.566 C 234.292 6.663,230.109 5.365,214.006 3.470 C 200.434 1.873,162.341 1.658,148.500 3.099">
                </path>
            </mask>
        </defs>
        <image loading="lazy" data-href="https://images.pexels.com/photos/910411/pexels-photo-910411.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="" width="100%" height="100%" mask="url(#bg-c)" preserveAspectRatio="xMidYMid slice">    
        </image>
    </svg>
</div>

CodePudding user response:

You can avoid duplicate <path> elements by creating a reusable definition in <defs>.

<defs>
   <path id="maskPath" d="..." />
</defs>

Note: your reusable path definition must not contain any fill property if you need to change it's fill for different instances (like mask and background shape)

Your mask would look something like this:

  <mask id="bg-c" >
    <use href="#maskPath" fill="white" />
  </mask>

Example

//emulate loading by setTimeout delay
emulateLazyLoad();

function emulateLazyLoad() {
  let lazyImages = document.querySelectorAll('[loading="lazy"]');
  lazyImages.forEach(function(lazy) {
    let imgHref = lazy.dataset.href;
    setTimeout(function() {
      if (lazy.nodeName.toLowerCase() == "image") {
        lazy.setAttribute("href", imgHref);
      } else {
        lazy.src = imgHref;
      }
      lazy.classList.replace("loading", "loaded");
    }, 1000);
  });
}
svg {
  width: 50%;
  display: inline-block;
}

.image {
  transition: 0.3s opacity;
}

.loading {
  opacity: 0;
}

.loaded {
  opacity: 1;
}
<div >
  <svg xmlns="http://www.w3.org/2000/svg" image-rendering="optimizeQuality" viewBox="0 0 510 360" shape-rendering="geometricPrecision" fill-rule="evenodd">
    <defs>
      <path id="maskPath" d="M148.500 3.099 C113.488 6.747,84.700 13.892,62.351 24.482 C 43.108 33.600,33.681 41.189,22.444 56.609 C 7.759 76.760,4.338 86.781,5.282 106.890 C 5.880 119.655,7.968 128.762,13.637 143.340 C 23.834 169.561,23.443 167.883,23.464 185.500 C23.479 197.898,23.041 203.414,21.520 210.000 C 18.603 222.635,18.745 240.097,21.847 249.975 C 30.657 278.033,52.991 299.700,93.500 319.490 C109.905 327.505,121.171 331.756,142.440 337.958 C 190.118 351.861,258.762 358.886,289.318 352.989 C 307.253 349.528,331.710 340.925,364.262 326.626 C392.030 314.428,408.965 308.298,425.480 304.468 C 451.051 298.538,471.322 283.403,481.509 262.633 C 487.363 250.696,489.054 243.962,489.701 230.000 C490.547 211.754,486.958 197.061,477.358 179.483 C 469.662 165.389,471.689 154.395,491.588 102.309 C 506.590 63.041,509.743 48.582,505.331 39.285 C501.149 30.471,482.609 22.301,459.167 18.940 C 445.334 16.957,405.463 18.286,371.500 21.862 C 319.125 27.376,299.077 27.919,277.500 24.407 C 270.080 23.199,265.779 21.647,253.000 15.566 C 234.292 6.663,230.109 5.365,214.006 3.470 C 200.434 1.873,162.341 1.658,148.500 3.099" />
      <mask id="bg-c" data-maskContentUnits="objectBoundingBox">
        <use href="#maskPath" data-transform="scale(0.001965, 0.002800)" fill="white" />
      </mask>
    </defs>
    <use id="bgShape" href="#maskPath" fill="gray" />
    <text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle">Loading ...</text>
    <image  loading="lazy" data-href="https://images.pexels.com/photos/910411/pexels-photo-910411.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="" width="100%" height="100%" mask="url(#bg-c)" preserveAspectRatio="xMidYMid slice" />
   
  </svg>
</div>

Place another <use>instance of your blob shape to create a background element like so:

<use id="bgShape" href="#maskPath" fill="gray" />
<text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle">Loading ...</text>
<image  loading="lazy" data-href="https://images.pexels.com/photos/910411/pexels-photo-910411.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="" width="100%" height="100%" mask="url(#bg-c)" preserveAspectRatio="xMidYMid slice" />
  • Related