HTML Masks - Saving precious kilobytes

HTML Masks - Saving precious kilobytes

Most of you probably heard the story about the first, crucial 8 seconds on WWW websites - so little time to attract the visitor and keep him from surfing somewhere else. Chances are raising, if during those 8 seconds some pretty images appear in front of his eyes!

During recent making of website using animated semi-transparent images in the header, I faced the problem of “heaviness” of the graphic assets. Even without massive scripts the page’s size was 2MB. It was obligatory to do something with it, concerning not only the load time, but also the server costs. Masks helped – a lot.

What are masks?

Whoever uses Photoshop or other graphic design tool knows what substractive masks are. To explain it easily: it’s a black-and-white layer, which defines the visibility of images in its area. Black means invisible, white – visible (sometimes vice versa; depending on whether we use substractive or additive mode). The important thing is the existence of average states – the whole greyscale can define the partial visibility of masked image.

Base PNG24

spidey

 

Size: 233.6KB

JPG + PNG24

Pseudo-mask – putting background in the front and making the hole inside. Can save you some space only in case of a static, one-coloured background. Effect is similar to a normal JPG picture, but with slightly more distinctive edges.

spidey-jpg2spidey-mask1

 

Size: 78.2KB (-66.5% but fake one)

Example code

<!-- CSS -->
<style>
.imageWrap{
     position: relative;
}
.image{
     z-index: 20;
}
.imageMask{
     position: absolute;
     top: 0;
     left: 0;
     z-index: 30;
}
</style>
<!-- HTML -->
<div class="imageWrap">
   <img class="image" alt="" src="IMAGE_URL" />
   <img class="imageMask" alt="" src="MASK_URL" />
</div>

Pros

  • Works everywhere
  • More defined edges than in normal JPG pic

Cons

  • It’s not a mask, it’s just a hole in a background.
  • Primitive way / a cheap trick
  • Can be only applied on static backgrounds
  • Almost as good as saving simple JPG

SVG + CSS3

Hard case here – Firefox browser doesn’t really understand any other way of using masks in CSS3 than via SVG. Fortunately, we can cheat a little bit by putting PNG inside the SVG. The second problem is the reaction to image’s width changes, but we can use jQuery in this case. Function showed in the example refreshes the mask width everytime the image’s width is altered.


spidey-jpg2

 

Size: 71.3KB (-69.4%)

Example code

<!-- JS -->
<script>
function resizeSVG(svg,image){
   $(svg).find('image').attr({'width': $(image).width()+'px','height': $(image).height()+'px'});
}
$(window).load( function(){
  resizeSVG('#svgMask','#svgMaskImage');
});
$(window).resize(function() {
  //zalecam wpierw użyć debouncing
  resizeSVG('#svgMask','#svgMaskImage');
});
</script>
<!-- CSS -->
<style>
  #svgMaskImage {
    mask: url('#svgMask');
    -webkit-mask: url('LOCAL_MASK_URL.png') top left / cover;
    -o-mask: url('LOCAL_MASK_URL.png') top left / cover;
    -ms-mask: url('LOCAL_MASK_URL.png') top left / cover;
  }
</style>
<!-- HTML -->
<img id="svgMaskImage" src="IMAGE_URL.jpg" width="X" height="Y" >
<svg width="0" height="0">
   <filter id="maskfilter"><feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0" /></filter>
   <mask id="svgMask">
      <image xlink:href="LOCAL_MASK_URL.png" filter="url(#maskfilter)" width="Xpx" height="Ypx" />
   </mask>
</svg>

Pros

  • Based on CSS3. That’s the future!

Cons

  • Slows down Firefox
  • Not working on IE… as it seems.
  • Digging deep in SVG and using hacks (svg filter)

Canvas Masking (HTML5)

Script based on Canvas Masking by codepo8, enhanced a little bit in Firefox, where the image was transformed before the mask loaded and some boo-boo appeared. Mask need to be loaded before the canvas transformation, then hid it on the bottom or remove it from DOM.


spidey-jpg2

 

Size: 70.7KB (-69.7%)

Example code

<!-- JS -->
<script>
(function(){
  var imagecanvas = document.createElement('canvas');
  var imagecontext = imagecanvas.getContext('2d');
  var images = document.querySelectorAll('.canvas-mask');
  var all = images.length;
  var img = false;
  var mask = false;
  var width = 0;
  var height = 0;
  window.addEventListener('load', function(ev){
    while (all--) {
      img = images[all];
      mask = document.createElement('img');
      mask.src = img.getAttribute('data-mask');
      width = img.getAttribute('width');
      height = img.getAttribute('height');
      imagecanvas.width = width;
      imagecanvas.height = height;
      imagecontext.drawImage(mask, 0, 0, width, height);
      imagecontext.globalCompositeOperation = 'source-atop';
      imagecontext.drawImage(img, 0, 0);
      img.src = imagecanvas.toDataURL();
    }
  }, false);
})();
</script>
<!-- CSS -->
<style>
.wrapper{
  position: relative;
}
.mask-layer{
  position: absolute; 
  top: 0; 
  left: 0; 
  z-index: 0;
}
.canvas-mask{
  z-index: 30
}
</style>
<!-- HTML -->
<div class="wrapper">
<img src="MASK_URL" alt="" class="mask-layer" /> <!-- Firefox FIX -->
<img src="IMAGE_URL" data-mask="MASK_URL" width="X" height="Y" class="canvas-mask" alt="" />
</div>

Pros

  • Works on almost every browser
  • Works pretty fast, too (GPU-based).

Cons

  • Based on JS, performed after tje images load.
  • Need to be loaded before the image changes its width/height in CSS.
  • Dynamic content may cause it to not load on time.

- Add comment -

/ required /
/ required, not public /