Kawałek Kodu

To już drugi wpis dotyczący podglądactwa. Dziś jednak spojrzymy szerzej, nie na sam temat, ale na obszar podglądania. Użyjemy innej, fajniejszej lupy niż w jednym z poprzednich wpisów. Będzie prawie jak ta systemowa!

Lepiej niż screenshot.

Chyba każdy projektant stron zastanawiał się czasem czy jest możliwe wykonanie czegoś w rodzaju zrzutu graficznego wycinka strony. Co prawda istnieją biblioteki, które uznaje się, że posiadają takie możliwości, np. html2canvas, tyle, że tak naprawdę ta biblioteka nie wykonuje zrzutu, ale odwzorowuje stronę na elemencie CANVAS. I to nie w 100% sposób. Jak się okazuje istnieje możliwość wykonania kopii fragmentu wygenerowanej strony. Niestety obecnie jest wspierana tylko przez przeglądarkę Firefox (również mobilną). Jeśli więc będziesz chciał zobaczyć efekt z dzisiejszego wpisu i nie masz Firefox, instaluj i wracaj do czytania dalszej części.

Funkcja, którą mam na myśli nazywa się: element i blisko współpracuje z właściwością background (dokładnie background-image). Jako jej argument podajemy id elementu (wraz z hash, czyli jako selektor CSS), którego chcemy wykonać screenshot, czyli tak:

#div1{
  background: element(#div2);
}

Tym sposobem element o id=div1, będzie miał tło, które jest screenshotem elementu id=div2. Jednakże ze względu na to, że obecnie funkcja jest wspierana tylko przez przeglądarkę Firefox, musimy używać jej w postaci z prefiksem: -moz-element.

Skala 1:1.

Sprawdźmy najpierw jak to w ogóle działa w praktyce, no i jak wygląda. Będziemy potrzebować prosty HTML, który będziemy kopiować i element, do której wkopiujemy fragment grafiki.

<body id="body">
  Tu wypełniamy czym chcemy
</body>

+

#zoom {
  position: absolute;
  left: 50px;
  top: 50px;
  border: 1px solid #000;
  width: 128px;
  height: 128px;
  background: -moz-element(#body) no-repeat, #fff;
}

#zoom będzie naszym elementem, którego tło będzie kopią body. Ponieważ musimy użyć selektora #id, więc BODY ma nadane id. Dlaczego #zoom ma dodatkowo nadane białe tło? Ponieważ bez tego, screenshot miałby przezroczyste tło, czyli pod elementem #zoom, byłoby widać faktyczną stronę. Pomimo, że BODY ma białe tło, to nie jest stricte ustawione w CSS. Możemy więc nadać je w ten sposób lub dla stylu BODY.

Jak widzisz pomimo, że BODY jest odsunięte od krawędzi przeglądarki, to zrzut jest umieszczony bezpośrednio w rogu elementu. Tak się dzieje ponieważ zrzut nie uwzględnia marginesów kopiowanego elementu.

Skala 4:1.

Powyższy przykład, to nasza podstawa do stworzenia lupy. Musimy dodać przesuwanie elementu #zoom oraz przesuwanie "wtłoczonego" do niego tła. Bo sama zmiana pozycji nie zapewni przesunięcia tła, co widać zresztą na powyższym przykładzie, gdzie element pomimo przesunięcia na stronie, ma tło zaczynające się od pozycji 0,0.

<body id="body">
  <div id="zoom" style="--scale:4"></div>
  Tu pozostała zawartość strony
</body>

Współczynnik skali umieszczamy w zmiennej CSS, dzieki temu uwalniamy się od modyfikacji wartości w arkuszu styli.

+

html,
body {
  min-height: 100%;
/* ukrywamy oryginalny
   kursor, teraz będzie
   nim element #zoom
*/
  cursor: none;
}

#zoom {
/* ustawiamy na fixed,
   dzięki temu będzie
   poza naturalnym flow
   elementów
*/
  position: fixed;
  left: 0;
  top: 0;
/* border możemy użyć tylko
   w rozmiarze 1px, więc
   po skali miałaby 4px
   grubości, zamiast tego
   używamy cienia
*/
  box-shadow: 0 0 0 calc(1px / var(--scale)) #000;
  width: calc(128px / var(--scale));
  height: calc(128px / var(--scale));
/* robimy przezroczysty
   dla najechania myszą
*/
  pointer-events: none;
/* to już szkiełko
   samej lupy, które
   skalujemy x4
*/
  background: -moz-element(#body) no-repeat, #fff;
  transform: scale(var(--scale));
}

+

document.addEventListener('mousemove', function(e) {
  const zoom = document.getElementById('zoom');
  const bodyDimensions = document.body.getBoundingClientRect();
  const mouseX = e.clientX - document.body.scrollLeft;
  const mouseY = e.clientY - document.body.scrollTop;
  zoom.style.left = (mouseX - zoom.offsetWidth / 2) + 'px';
  zoom.style.top = (mouseY - zoom.offsetHeight / 2) + 'px';
  zoom.style.backgroundPositionX =
   (-mouseX + bodyDimensions.left + zoom.offsetWidth / 2) + 'px';
  zoom.style.backgroundPositionY =
   (-mouseY + bodyDimensions.top + zoom.offsetHeight / 2) + 'px';
});

CSS i HTML są chyba jasne, więc zajmijmy się JavaScript. Dodaliśmy tu obsługę zdarzenia mousemove, dzięki czemu możemy przesuwać nasz element za myszą. Uwzględniamy przesunięcie strony. Aby lupa nie wisiała w lewym, górnym punkcie kursora, ale była wyśrodkowana względem niego, odejmujemy połowę jej rozmiarów. W drugiej części ustawiamy pozycję tła, czyli naszego screenshota. Jeśli przesuwamy lupę w prawo, to tło w lewo. Uwzględniamy tu przesunięcie BODY (czyli wcześniej wspomniane marginesy, które odsuwają BODY od krawędzi) oraz środkujemy tło, tak aby środek lupy wskazywał na punkt, który powiększamy. Wystarczy zmienić wartość zmiennej --scale, aby lupa zyskała inne powiększenie. Nic nie stoi na przeszkodzie, aby używać jej również do pomniejszania! Wystarczy ustawić skalę np. 0.5.

Bonusem jest to, że screenshot nie jest wykonywany tak naprawdę na wyrenderowanym fragmencie, ale przed jego wyświetleniem, dzieki czemu przy powiększeniu nie mamy do czynienia z efektem pixelize, ale z fantastyczną jakością.

To jeszcze nie koniec z efektem lupy na stronie www, jeszcze raz do niej wrócimy. Do przeczytania!

 

Przydatne linki:
Funkcja element z CSS.
Przykład 1 - użycie funkcji element().
Przykład 2 - lupa z wykorzystaniem funkcji element().
Zoom na lupę, czyli prosty (nie)plugin do powiększania obrazków.