Kawałek Kodu

Nie wiem kto jest autorem chińskiego przysłowia sparafrazowanego w tytule wpisu, ale gdyby żył dziś, w epoce komputerów, minimalizacji i wszędobylskiej optymalizacji, to pewnie właśnie takie zmodyfikowane powiedzenie przyszłoby mu do głowy. Pogadamy dziś o background-image dla obrazka, pseudometodzie na złodziei oraz karuzeli, czyli nieskończenie przewijanym sliderze.

Jak hologram.

Hologram charakteryzuje się tym, że jego mały fragment stanowi odzwierciedlenie całości. Czyli mamy do czynienia z pewnego rodzaju obrazem w obrazie. Niby jeden, a więcej. Podobnie jest z obrazkiem, czyli tagiem IMG. Standardowo stanowi on po prostu... obrazek. Ale czy miałby on coś przeciwko, aby wcisnąć do niego jeszcze jeden, albo i więcej obrazków? Źródło mamy jedno, więc kaszana. Można kombinować z pseudoelementami dla obrazka, które opisywałem we wpisie Yeti - jedni widzieli, inni nie, czyli pseudoelement dla IMG, ale nie jest to metoda 100% pewna. Ale, ale, przecież mamy jeszcze właściwość background-image. Tyle, że kiedy ją dodamy, to takie obrazkowe tło znajdzie się pod faktyczną zawartością obrazu, więc nic nie zobaczymy. Potrzebna byłaby swego rodzaju gumka, która pomoże nam wymazać faktyczną zawartość, a pokazać oczekiwane tło.

Naszym gumowym pomocnikiem będzie dziś właściwość object-position, o której również wspominałem w Upychanie walizki, czyli o właściwości object-fit i object-position. Ale ta właściwość nie zadziała kiedy zawartość obrazka będzie mieć takie same wymiary jak obrazek (kontener), bo nie ma wtedy możliwości manewrowania tąże zawartością - nie przesuniemy jej, bo brak nadmiarowych lub pustych przestrzeni.
Z czym to się wiąże? Wymiary obrazka nadamy albo poprzez atrybuty HTML, albo poprzez właściwości CSS, a co za tym idzie nie będą one współgrać z faktycznymi rozmiarami źródła, a właściwie z proporcjami. Po prostu bardziej będziemy się starać przystosować wymiary obrazka (kontenera) pod tło, a nie pod faktyczną zawartość. Może przykład:

<img src="https://picsum.photos/id/237/200/300" />

+

img {
  object-position: -100% 0;
  object-fit: contain;
  width: 200px;
  height: 200px;
}

Realna zawartość obrazka ma wymiary 200x300. My ustawiamy obrazek na 200x200, a ponieważ wciskamy zawartość tak aby widać było całą, pozostają nam po lewej i prawej marginesy po 50px. Tak jest przy domyślnym ustawieniu object-position, czyli 50% 50%. My przesunęliśmy o 150% w lewo względem domyślnego ustawienia, więc o 150px (50px i jeszcze 100px). Jeśli przesuniemy jeszcze w lewo, to zawartość całkowicie zniknie, czyli osiągniemy efekt gumki myszki! Teraz wystarczy podstawić do obrazka tło i gotowe.

img {
  object-position: -300% 0;
  object-fit: contain;
  width: 200px;
  height: 200px;
  background-image: url(https://picsum.photos/id/1011/200);
}

Metoda oczywiście nie jest odpowiednią dla celów SEO, ale jeśli stosujemy ją dla zawartości niedostępnej dla robota, to nie ma to znaczenia. To co przy okazji tu zyskujemy, to sposób na pseudoochronę przed złodziejami zdjęć. Kliknij na obrazek i wybierz opcję zapisu na dysk...

Karuzela co niedziela.

Ten wpis powstał ze zlepku dwóch pomysłów. Pierwszym było używanie tła dla obrazków, czyli sekcja wyżej, a drugim slider oparty o jeden plik graficzny. Ale ponieważ sliderów w CSS jest pod dostatkiem (ewentualnie przyjdzie jeszcze czas na osobny wpis), to pomysł przekształcił się na efekt karuzeli, czyli slidera wyświetlającego jeden lub zazwyczaj więcej obrazków w nieskończonej pętli.

Karuzela kiełkowała wtedy, kiedy zacząłem myśleć, że można wykorzystać w niej jeden długaśny obrazek ze sklejonych ze sobą slajdów, zamiast odrębnych plików graficznych. Jeśli będą tych samych rozmiarów, to realizacja będzie ułatwiona. I tak jak wyżej, pomyślałem o użyciu object-position. Zawartość byłaby dłuższa od samego kontenera (IMG), więc możemy wykorzystać tą właściwość, szczególnie w połączeniu z object-fit: cover. Po prostu w kontenerze byłby wykadrowany fragment z tego długasa, a dzięki object-position przesuwalibyśmy się po kolejnych jego slajdach.

Problem zaczyna się robić wtedy kiedy dochodzimy do ostatniego slajdu i chcemy przewinąć w lewo (albo pierwszego i w prawo jeśli karuzela przewija się w prawo). Zaczyna brakować nam kolejnego slajdu, a właściwie zapętlenia. Za ostatnim slajdem nie zobaczymy pierwszego, bo używanie właściwości object-* nie powoduje zawijania zawartości. W tym momencie pomyślałem, że tą dziurę można zalepić używając background-image - skoro można, to dlaczego nie użyć. I tak też zrobiłem. Karuzela działała, ale nadal coś mi nie dawało spokoju. Bo skoro tło może się zawijać - mamy background-repeat, zrestą domyślnie, to jaki jest sens łączenia obydwu metod. Możemy główną zawartość przesunąć poza kontener, aby w ogóle nie była widoczna, a przewijać tylko tło!

<img src="logos-20.png" alt="Logotypy" />

+

img {
  object-fit: cover;
  object-position: -100% 0;
  width: 360px;
  height: 120px;
  animation: slide 20s linear infinite;
  background: url(logos-20.png);
  background-size: cover;
}

@keyframes slide {
  0% {
    background-position: 0 0;
  }

  100% {
    background-position: calc(100% + ((100% / (20 - 3)) * 3)) 0;
  }
}

Kod HTML chyba nie wymaga wyjaśnień. No, może oprócz tego, że pod względem SEO nie ma możliwości indeksacji każdego ze slajdów z osobna. Taka karuzela przyda się raczej tam gdzie nas to nie interesuje, a w szczególności dla pokazania przewijanej listy partnerów lub typów obsługiwanych płatności. Ale będzie to efekt i tak przyjaźniejszy dla celów pozycjonowania od wcześniejszego przykładu, bowiem plik dla tła jest identyczny dla pliku zawartego w src obrazka - tak więc jako całość, zaindeksuje się. Gwoli ścisłości - Googlebot nie indeksuje obrazków, które są tłami.
Jeśli chodzi o CSS, to jak już wcześnie pisałem, z pomocą object-fit:cover wyrzucamy zawartość poza kontener, będziemy pokazywać samo tło. Użyłem tu obrazków o wymiarach 360x360, które w ilości 20 sztuk skleiłem w jednego długasa. Ustalam wysokość karuzeli na 120px, więc obrazek skaluje się do 33.33%. Szerokość kadru karuzeli to 360px, więc domyślnie widać 3 slajdy.
W tym momencie karuzela była statyczna, więc dodałem animację. Ustawienia animacji są dosyć jasne, oprócz docelowej wartości background-position. Skąd ona się wzięła? Przesunięcie do 100% oznacza, że w ostatniej klatce animacji zobaczymy 3 ostatnie slajdy, tzn. prawa krawędź tła zrówna się z prawą krawędzia kontenera. A my musimy przesunąć się jeszcze o te 3 slajdy w lewo. Może lepiej zobrazuje to... obrazek.

Na pierwszym szkicu mamy sytuację kiedy background-position ma wartość 100%. Na drugim jest to sytuacja docelowa. Będąc w 100% musimy więc "pociągnąć" nasze tło o trzykrotną (bo widzimy w kadrze 3 slajdy) szerokość pojedynczego slajdu poza kadrem. Poza kadrem mamy 7-3=4 slajdy. Jeden slajd ma szerokość 25%. Tak więc 3*25%=75%, co razem dałoby przesunięcie 175% w ostatniej klatce animacji.

Ogólny wzór jest taki:
x=100%+(100%/(ilosc_wszystkich_slajdow - ilosc_widoczna_w_kadrze)*ilosc_widoczna_w_kadrze) i wynika z niego również, że widoczny może być 1 slajd.

W działaniu:

Ładnie, pięknie, ale karuzela nie jest responsywna. Ustawiliśmy na sztywno rozmiary naszego obrazka. Możemy ją jednak przekształcić w prosty sposób w responsywną, ale niestety! będzie trzeba użyć dodatkowego tagu. Potrzebujemy jakiegoś kontenera oraz coś, co zapewni nam odpowiednie proporcje. Wykorzystamy tu element DIV z trickiem na utrzymanie poziomego aspect-ratio przy pomocy padding-top.

<div>
  <img src="logos-20.png" alt="Logotypy" />
</div>

+

div {
  width: 100%;
  padding-top: calc(100% / 3);
  position: relative;
}

img {
  object-fit: cover;
  object-position: -100% 0;
  width: 100%;
  height: 100%;
  animation: slide 20s linear infinite;
  background: url(logos-20.png);
  background-size: cover;
  position: absolute;
  left: 0;
  top: 0;
}

Ponieważ pionowe padding (tu top) jest zależne od szerokości, z łatwością ustalamy, że wysokość kontenera ma mieć proporcje 3:1, albo jak kto woli 1:1/3. Ponieważ DIV zapewni nam proporcje, to wymiary obrazka śmiało ustawiamy na 100%. Aby zniwelować przestrzeń zajętą przez padding, ustalamy pozycjonowanie obrazka na absolutne, pamiętając o pozycji relatywnej dla kontenera.

Jeśli jeszcze nie zakręciło Ci się w głowie, to zapraszam Cię na kolejny wpis. Do przeczytania!

 

Przydatne linki:
Responsywna karuzela z jednym zdjęciem.
Upychanie walizki, czyli o właściwości object-fit i object-position.
Yeti - jedni widzieli, inni nie, czyli pseudoelement dla IMG.
Kto ma rację, czyli aspect ratio dla elementów HTML.
O indeksowaniu obrazków w Google.
Wszechświat hologramem?