Tag Archives: svg

Maski w HTML - Oszczędzamy kilobajtyHTML Masks - Saving precious kilobytes

Tworząc jedną ze stron z wykorzystaniem ruchomych półprzeźroczystych obrazów w nagłówku, borykałem się z problemem “ciężkości” całej grafiki. Strona zajmowała ponad 2MB, nie posiadając żadnych większych skryptów, więc zarówno dla zminimalizowania kosztów za serwer, jak i szybkości wczytywania strony wypadało coś z tym zrobić. Z pomocą przyszły maski.

Czym są maski?

Kto korzysta z Photoshopa czy innego programu graficznego ten wie czym jest maska przycinająca. Najprościej tłumacząc to czarno-biała warstwa, definiująca widoczność obrazów w obszarze jej działania. To co czarne – niewidoczne, to co białe – widoczne (czasem jest na odwrót – zależnie czy to maska przeźroczystości czy maska jasności). Co ważne, występować mogą stany pośrednie, reprezentowane przez skalę szarości, powodujące częściowe wyświetlenie obrazu pod maską.

Bazowe PNG24

spidey

 

Rozmiar: 233.6KB

JPG + PNG24

Pseudo-maska, czyli wywinięcie backgroundu na wierzch i zrobienie w nim dziury na obrazek z większym detalem. Można zaoszczędzić coś miejsca tylko w przypadku statycznego, jednokolorowego tła. Efekt zbliżony do zwykłego jpg-a, ale z bardziej wyraźnymi krawędziami.

spidey-jpg2spidey-mask1

 

Rozmiar: 78.2KB (-66.5% ale udawane)

Przykładowy kod

<!-- 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>

Zalety

  • Działa wszędzie
  • Ostrzejsze krawędzie niż w zwykłym JPG

Wady

  • To nie jest maska tylko zwykła dziura w tle
  • Prymitywne rozwiązanie / oszukaństwo
  • Tylko na statycznym tle
  • Niemal równie dobrze można zapisać samego JPG

SVG + CSS3

Tutaj sprawa jest ciężka, wszystko przez Firefoxa, który nie rozumie używania masek w CSS3 inaczej niż poprzez SVG. Na szczęście można go oszukać osadzając PNG wewnątrz SVG. Drugim problemem jest reagowanie na zmiany szerokości przycinanego obrazu, do tego można użyć jQuery. Funkcja w przykładzie wraz ze zmianą szerokości obrazka odświeża szerokość maski.


spidey-jpg2

 

Rozmiar: 71.3KB (-69.4%)

Przykładowy kod

<!-- 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>

Zalety

  • Oparty na CSS3 – przyszłościowy

Wady

  • Spowalnia Firefoxa
  • Zdaje się nie działać pod IE :(
  • Babranie się w SVG i stosowanie hacków (svg filter)

Canvas Masking (HTML5)

Skrypt bazujący na Canvas Masking by codepo8, poprawiony w Firefoxie, gdzie obraz był przekształcany zanim załadowała się maska i zwracał babole. Maskę należy wcześniej wczytać, a po przekształceniu canvasowym ukryć pod spód lub usunąć z DOM’a.


spidey-jpg2

 

Rozmiar: 70.7KB (-69.7%)

Przykładowy kod

<!-- 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>

Zalety

  • Działa na większości przeglądarek
  • W miarę szybkie działanie, bo wspierane przez GPU.

Wady

  • Oparte na JS, wykonuje się po załadowaniu obrazów.
  • Trzeba ładować zanim obraz zmieni szerokość/wysokość w CSS.
  • Zdarza się, że nie zdąży się załadować przy dynamicznym contencie na stronie.

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.