Xlera8

Fantazyjny efekt najechania kursorem dla Twojego awatara

Czy znasz ten rodzaj efektu, w którym czyjaś głowa wystaje przez okrąg lub dziurę? Słynna animacja Porky Pig, w której macha na pożegnanie, wyskakując z serii czerwonych pierścieni, jest doskonałym przykładem Kilian Valkhof faktycznie odtworzył to tutaj na CSS-Tricks jakiś czas temu.

Mam podobny pomysł, ale rozwiązałem go w inny sposób i z odrobiną animacji. Myślę, że jest to całkiem praktyczne i zapewnia zgrabny efekt najechania kursorem, którego można użyć na czymś takim jak własny awatar.

Zobaczyć, że? Zrobimy animację skalowania, w której awatar wydaje się wyskakiwać prosto z kręgu, w którym się znajduje. Fajnie, prawda? Nie patrz na kod i zbudujmy razem tę animację krok po kroku.

HTML: Tylko jeden element

Jeśli nie sprawdziłeś kodu dema i zastanawiasz się ile divs to zajmie, a potem zatrzymaj się na tym miejscu, ponieważ nasze znaczniki to tylko pojedynczy element obrazu:

<img src="" alt="">

Tak, jeden element! Trudną częścią tego ćwiczenia jest użycie najmniejszej możliwej ilości kodu. Jeśli byłeś podążaj za mną przez jakiś czas powinieneś się do tego przyzwyczaić. Staram się znaleźć rozwiązania CSS, które można osiągnąć przy użyciu możliwie najmniejszego, najłatwiejszego w utrzymaniu kodu.

Pisałem seria artykułów tutaj na CSS-Tricks, gdzie badam różne efekty najechania kursorem przy użyciu tego samego znacznika HTML zawierającego pojedynczy element. Szczegółowo omawiam gradienty, maskowanie, przycinanie, kontury, a nawet techniki układu. Gorąco polecam ich sprawdzenie, ponieważ ponownie wykorzystam wiele sztuczek w tym poście.

Plik obrazu, który jest kwadratowy z przezroczystym tłem, będzie najlepiej pasował do tego, co robimy. Oto ten, którego używam, jeśli chcesz zacząć od tego.

Zaprojektowany przez cang

Mam nadzieję, że zobaczę wiele przykładów tego, jak to możliwe, używając prawdziwych obrazów — więc podziel się końcowym wynikiem w komentarzach, gdy skończysz, abyśmy mogli zbudować kolekcję!

Zanim przejdziemy do CSS, najpierw przeanalizujmy efekt. Obraz powiększa się po najechaniu myszką, więc na pewno skorzystamy transform: scale() tam. Za awatarem znajduje się okrąg, a gradient radialny powinien załatwić sprawę. Na koniec potrzebujemy sposobu na utworzenie obramowania na dole koła, które tworzy wygląd awatara za okręgiem.

Chodźmy do pracy!

Efekt skali

Zacznijmy od dodania transformacji:

img { width: 280px; aspect-ratio: 1; cursor: pointer; transition: .5s;
}
img:hover { transform: scale(1.35);
}

Jeszcze nic skomplikowanego, prawda? Przejdźmy dalej.

Okrąg

Powiedzieliśmy, że tło będzie radialnym gradientem. To idealne rozwiązanie, ponieważ między kolorami gradientu radialnego możemy tworzyć twarde stopnie, które sprawiają, że wygląda to tak, jakbyśmy rysowali okrąg liniami ciągłymi.

img { --b: 5px; /* border width */ width: 280px; aspect-ratio: 1; background: radial-gradient( circle closest-side, #ECD078 calc(99% - var(--b)), #C02942 calc(100% - var(--b)) 99%, #0000 ); cursor: pointer; transition: .5s;
}
img:hover { transform: scale(1.35);
}

Zwróć uwagę na zmienną CSS, --b, używam tam. Reprezentuje grubość „obramowania”, które tak naprawdę jest używane do definiowania twardych punktów zatrzymania koloru dla czerwonej części gradientu promieniowego.

Następnym krokiem jest zabawa z rozmiarem gradientu po najechaniu myszką. Okrąg musi zachować swój rozmiar w miarę wzrostu obrazu. Ponieważ stosujemy a scale() transformacji, tak naprawdę musimy spadek rozmiar koła, ponieważ w przeciwnym razie skaluje się wraz z awatarem. Tak więc, podczas gdy obraz skaluje się w górę, potrzebujemy zmniejszenia gradientu.

Zacznijmy od zdefiniowania zmiennej CSS, --f, który definiuje „współczynnik skali” i użyj go do ustawienia rozmiaru okręgu. używam 1 jako wartość domyślna, ponieważ jest to początkowa skala obrazu i okręgu, z którego dokonujemy transformacji.

Oto demo ilustrujące tę sztuczkę. Najedź kursorem, aby zobaczyć, co dzieje się za kulisami:

Dodałam trzeci kolor radial-gradient aby lepiej zidentyfikować obszar gradientu po najechaniu kursorem:

radial-gradient( circle closest-side, #ECD078 calc(99% - var(--b)), #C02942 calc(100% - var(--b)) 99%, lightblue
);

Teraz musimy ustawić nasze tło na środku koła i upewnić się, że zajmuje całą wysokość. Lubię deklarować wszystko bezpośrednio na background właściwość skrótu, więc możemy dodać nasze pozycjonowanie w tle i upewnić się, że się nie powtórzy, zaznaczając te wartości zaraz po radial-gradient():

background: radial-gradient() 50% / calc(100% / var(--f)) 100% no-repeat;

Tło jest umieszczane na środku (50%), ma szerokość równą calc(100%/var(--f))i ma wysokość równą 100%.

Nic się nie skaluje, kiedy --f jest równe 1 — znowu nasza początkowa skala. Tymczasem gradient zajmuje całą szerokość kontenera. Kiedy wzrastamy --f, rozmiar elementu rośnie — dzięki scale() transform — a rozmiar gradientu zmniejsza się.

Oto, co otrzymamy, gdy zastosujemy to wszystko do naszego demo:

Jesteśmy coraz bliżej! Mamy efekt przepełnienia na górze, ale nadal musimy ukryć dolną część obrazu, aby wyglądało to tak, jakby wyskakiwał z koła, a nie siedział przed nim. To jest najtrudniejsza część całej tej sprawy i to właśnie zamierzamy zrobić dalej.

Dolna granica

Najpierw próbowałem sobie z tym poradzić za pomocą border-bottom właściwość, ale nie mogłem znaleźć sposobu na dopasowanie rozmiaru obramowania do rozmiaru koła. Oto najlepsze, co mogłem uzyskać i od razu widać, że jest źle:

Właściwym rozwiązaniem jest użycie outline nieruchomość. Tak, outline, Nie border, w poprzedni artykuł, pokazuję jak outline jest potężny i pozwala nam tworzyć fajne efekty zawisu. W połączeniu z outline-offset, mamy dokładnie to, czego potrzebujemy do naszego efektu.

Chodzi o to, aby ustawić outline na obrazie i dostosuj jego przesunięcie, aby utworzyć dolną ramkę. Przesunięcie będzie zależeć od współczynnika skalowania w taki sam sposób jak rozmiar gradientu.

Teraz mamy naszą dolną „granicę” (właściwie an outline) w połączeniu z „obramowaniem” utworzonym przez gradient, aby utworzyć pełne koło. Nadal musimy ukryć fragmenty outline (z góry i z boków), do którego przejdziemy za chwilę.

Oto nasz dotychczasowy kod, w tym kilka dodatkowych zmiennych CSS, których możesz użyć do skonfigurowania rozmiaru obrazu (--s) i kolor „obramowania” (--c):

img { --s: 280px; /* image size */ --b: 5px; /* border thickness */ --c: #C02942; /* border color */ --f: 1; /* initial scale */ width: var(--s); aspect-ratio: 1; cursor: pointer; border-radius: 0 0 999px 999px; outline: var(--b) solid var(--c); outline-offset: calc((1 / var(--f) - 1) * var(--s) / 2 - var(--b)); background: radial-gradient( circle closest-side, #ECD078 calc(99% - var(--b)), var(--c) calc(100% - var(--b)) 99%, #0000 ) 50% / calc(100% / var(--f)) 100% no-repeat; transform: scale(var(--f)); transition: .5s;
}
img:hover { --f: 1.35; /* hover scale */
}

Ponieważ potrzebujemy okrągłej dolnej krawędzi, dodaliśmy a border-radius na dolnej stronie, umożliwiając outline aby dopasować się do krzywizny gradientu.

Kalkulacja zastosowana na outline-offset jest dużo prostszy niż się wydaje. Domyślnie, outline jest narysowany zewnętrzne pudełka elementu. A w naszym przypadku jest to nam potrzebne zakładka element. Dokładniej, potrzebujemy go, aby podążał za okręgiem utworzonym przez gradient.

Diagram przejścia w tle.

Kiedy skalujemy element, widzimy przestrzeń między okręgiem a krawędzią. Nie zapominajmy, że chodzi o to, aby okrąg miał ten sam rozmiar po uruchomieniu transformacji skali, co pozostawia nam miejsce, którego użyjemy do zdefiniowania przesunięcia konturu, jak pokazano na powyższym rysunku.

Nie zapominajmy, że drugi element jest skalowany, więc nasz wynik też jest skalowany… co oznacza, że ​​musimy podzielić wynik przez f aby uzyskać rzeczywistą wartość przesunięcia:

Offset = ((f - 1) * S/2) / f = (1 - 1/f) * S/2

Dodajemy znak ujemny, ponieważ potrzebujemy, aby kontur przechodził od zewnątrz do wewnątrz:

Offset = (1/f - 1) * S/2

Oto krótkie demo, które pokazuje, jak kontur podąża za gradientem:

Być może już to widzisz, ale nadal potrzebujemy dolnego konturu, aby nachodził na okrąg, zamiast pozwolić mu przeniknąć przez niego. Możemy to zrobić, usuwając rozmiar obramowania z przesunięcia:

outline-offset: calc((1 / var(--f) - 1) * var(--s) / 2) - var(--b));

Teraz musimy dowiedzieć się, jak usunąć górną część z konturu. Innymi słowy, chcemy tylko dolnej części obrazu outline.

Najpierw dodajmy miejsce u góry z wypełnieniem, aby uniknąć nakładania się u góry:

img { --s: 280px; /* image size */ --b: 5px; /* border thickness */ --c: #C02942; /* border color */ --f: 1; /* initial scale */ width: var(--s); aspect-ratio: 1; padding-block-start: calc(var(--s)/5); /* etc. */
}
img:hover { --f: 1.35; /* hover scale */
}

Nie ma szczególnej logiki w tym górnym wypełnieniu. Chodzi o to, aby kontur nie dotykał głowy awatara. Użyłem rozmiaru elementu, aby zdefiniować, że przestrzeń ma zawsze tę samą proporcję.

Zauważ, że dodałem tzw content-box wartość do background:

background: radial-gradient( circle closest-side, #ECD078 calc(99% - var(--b)), var(--c) calc(100% - var(--b)) 99%, #0000 ) 50%/calc(100%/var(--f)) 100% no-repeat content-box;

Potrzebujemy tego, ponieważ dodaliśmy dopełnienie i chcemy, aby tło było ustawione tylko na polu zawartości, więc musimy wyraźnie powiedzieć, aby tło się tam zatrzymało.

Dodanie maski CSS do miksu

Dotarliśmy do ostatniej części! Wszystko, co musimy zrobić, to schować kilka elementów i gotowe. W tym celu będziemy polegać na mask właściwości i oczywiście gradienty.

Oto rysunek ilustrujący, co musimy ukryć lub co musimy pokazać, aby było dokładniejsze

Pokazuje sposób zastosowania maski do dolnej części koła.

Lewy obraz to to, co mamy obecnie, a prawy to to, czego chcemy. Zielona część ilustruje maskę, którą musimy nałożyć na oryginalny obraz, aby uzyskać efekt końcowy.

Możemy zidentyfikować dwie części naszej maski:

  • Okrągła część na dole, która ma taki sam wymiar i krzywiznę jak gradient radialny, którego użyliśmy do stworzenia okręgu za awatarem
  • Prostokąt u góry, który obejmuje obszar wewnątrz konturu. Zwróć uwagę, że kontur znajduje się poza zielonym obszarem u góry — jest to najważniejsza część, ponieważ umożliwia wycięcie konturu, tak aby widoczna była tylko dolna część.

Oto nasz końcowy CSS:

img { --s: 280px; /* image size */ --b: 5px; /* border thickness */ --c: #C02942; /* border color */ --f: 1; /* initial scale */ --_g: 50% / calc(100% / var(--f)) 100% no-repeat content-box; --_o: calc((1 / var(--f) - 1) * var(--s) / 2 - var(--b)); width: var(--s); aspect-ratio: 1; padding-top: calc(var(--s)/5); cursor: pointer; border-radius: 0 0 999px 999px; outline: var(--b) solid var(--c); outline-offset: var(--_o); background: radial-gradient( circle closest-side, #ECD078 calc(99% - var(--b)), var(--c) calc(100% - var(--b)) 99%, #0000) var(--_g); mask: linear-gradient(#000 0 0) no-repeat 50% calc(-1 * var(--_o)) / calc(100% / var(--f) - 2 * var(--b)) 50%, radial-gradient( circle closest-side, #000 99%, #0000) var(--_g); transform: scale(var(--f)); transition: .5s;
}
img:hover { --f: 1.35; /* hover scale */
}

Rozbijmy to mask nieruchomość. Na początek zauważ, że podobny radial-gradient() z background znajduje się tam nieruchomość. Stworzyłem nową zmienną, --_g, aby części wspólne były mniej zagracone.

--_g: 50% / calc(100% / var(--f)) 100% no-repeat content-box; mask: radial-gradient( circle closest-side, #000 99%, #0000) var(--_g);

Dalej jest a linear-gradient() tam też:

--_g: 50% / calc(100% / var(--f)) 100% no-repeat content-box; mask: linear-gradient(#000 0 0) no-repeat 50% calc(-1 * var(--_o)) / calc(100% / var(--f) - 2 * var(--b)) 50%, radial-gradient( circle closest-side, #000 99%, #0000) var(--_g);

Spowoduje to utworzenie prostokątnej części maski. Jego szerokość jest równa szerokości gradientu promieniowego minus dwukrotna grubość granicy:

calc(100% / var(--f) - 2 * var(--b))

Wysokość prostokąta jest równa połowie, 50%, rozmiaru elementu.

Potrzebujemy również gradientu liniowego umieszczonego w poziomym środku (50%) i przesunięcie od góry o taką samą wartość jak przesunięcie konturu. Stworzyłem kolejną zmienną CSS, --_o, dla przesunięcia, które wcześniej zdefiniowaliśmy:

--_o: calc((1 / var(--f) - 1) * var(--s) / 2 - var(--b));

Jedną z mylących rzeczy jest to, że potrzebujemy a ujemny przesunięcie konturu (aby przesunąć go z zewnątrz do wewnątrz), ale a pozytywny przesunięcie dla gradientu (aby przejść od góry do dołu). Więc jeśli zastanawiasz się, dlaczego mnożymy przesunięcie, --_o, przez -1, cóż, teraz już wiesz!

Oto demo ilustrujące konfigurację gradientu maski:

Najedź na powyższe i zobacz, jak wszystko porusza się razem. Środkowe pole ilustruje warstwę maski złożoną z dwóch gradientów. Wyobraź sobie to jako widoczną część lewego obrazu, a wynik końcowy otrzymasz po prawej!

Zamykając

Uff, skończyliśmy! I nie tylko udało nam się stworzyć zgrabną animację po najechaniu kursorem, ale zrobiliśmy to wszystko za pomocą jednego kodu HTML <img> element. Tylko to i mniej niż 20 linijek sztuczek CSS!

Jasne, polegaliśmy na kilku małych sztuczkach i formułach matematycznych, aby osiągnąć tak złożony efekt. Ale wiedzieliśmy dokładnie, co robić, ponieważ z góry zidentyfikowaliśmy potrzebne elementy.

Czy moglibyśmy uprościć CSS, gdybyśmy pozwolili sobie na więcej HTML? Absolutnie. Ale jesteśmy tutaj, aby nauczyć się nowych sztuczek CSS! To było dobre ćwiczenie do zbadania gradientów CSS, maskowania, outline zachowanie własności, transformacje i wiele więcej. Jeśli w którymś momencie poczułeś się zagubiony, zdecydowanie sprawdź moja seria posługujących się tymi samymi ogólnymi pojęciami. Czasami pomocne jest zobaczenie większej liczby przykładów i przypadków użycia, aby dotrzeć do celu.

Zostawię cię z ostatnią demonstracją, która wykorzystuje zdjęcia popularnych programistów CSS. Nie zapomnij pokazać mi demo z własnym obrazem, abym mógł dodać go do kolekcji!

Czat z nami

Cześć! Jak mogę ci pomóc?