Maski w HTML - Oszczędzamy kilobajty

Maski w HTML - Oszczędzamy kilobajty

Zapewne każdy z was zna przypowiastkę o pierwszych, kluczowych 8 sekundach na stronie www, w których to musisz zachęcić internautę by nie uciekł gdzie indziej. Szanse są większe, gdy w te 8 sekund zdąży zobaczyć ładne grafiki.

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.

- Dodaj komentarz -

/ wymagany /
/ wymagany, nie będzie publikowany /