Webfont + opacity czyli dziwne zachowanie czcionek w Google Chrome.

Właściwie każdy projekt, który wychodzi spod moich palców, gdzieś zaszytą ma jakąś animację z wykorzystaniem CSS3 transitions lub opartą na jQuery – proste przejście od opacity: 0 do opacity: 1, czy też animacje fadeIn/fadeOut to praktycznie standard. Rzadkością są też projekty, gdzie nie embedowałbym przynajmniej jednego kroju czcionki jako webfont. W jednym z realizowanych projektów zauważyłem, że te dwie przydatne praktyki nie lubią się na wzajem – a raczej, nie lubi ich Google Chrome. Czcionka zachowuje się po prostu… dziwnie.

W czym dokładnie problem?

Z nieznanych mi przyczyn, font renderowany z 100% opacity jest w dziwny sposób pogrubiony. Natomiast, kiedy zadziała na niego przezroczystość – pogrubienie znika. Powoduje to paskudny, pozbawiony płynności skok w wyglądzie tekstu. Nie lubię tego ja, nie lubiecie tego Wy, a najbardziej nie lubią tego klienci którzy chcą zobaczyć na stronie animacje płynne niczym Ciekawe jest, że problem ten nie występuje pod Safari – przynajmniej, w jego najnowszej wersji 6.0. Wydaje mi się więc, że problem został wyeliminowany w najświeższym WebKicie. Poniżej prosty przykład jak wygląda rendering fontu na Chrome (wersja 21):

Jaka jest różnica między tymi dwoma liniami? W pierwszej opacity nie jest w żaden sposób zmodyfikowane. W drugiej – opacity ustawiłem na 0.99. Poza tą różnicą – krój czcionki, grubość i rozmiar są identyczne. Aby lepiej to zobrazować, przygotowałem demo na jsfiddle: http://jsfiddle.net/przyb/9sEVV/

Rozwiązanie 1: zmiana opacity

Najprostszym rozwiązaniem jest to zastosowane w powyższym demo. Wystarczy na elemencie zamieszczonym wewnątrz animowanego elementu ustawić parametr opacity na wartość 0.99. Nie zmieni to w żaden widoczny sposób jego wyświetlania. Ale dokona zmiany w sposobie renderowania czcionki. Viola, nagle animacje są pozbawione brzydkiego efektu skoku. Inaczej mówiąc, jeśli nasz „fejdowany” div ma klasę .fade-me, a tekst w jego środku znajduje się w tagu <p>, to wystarczy nam taka linia w css, aby uratować świat:

.fade-me{ opacity: .99; }

Rozwiązanie 2: dziwne powiązanie z backface-visibility

Szukając przyczyny powyższego problemu, natknąłem się również na drugi ślad. Okazuje się, że to dziwne zachowanie powiązane jest w na pozór niewyjaśniony sposób z parametrem backface-visibility. Parametr ten odpowiada za widoczność „tyłu” elementu przy przekształceniach w trójwymiarze. Nie będę się wgłębiał w szczegóły – Chris Coyier opisał to ze szczegółami na CSS Tricks: http://css-tricks.com/almanac/properties/b/backface-visibility/

Przykład możecie zobaczyć na jsfiddle: http://jsfiddle.net/przyb/HPRhM/

Tu robi się już na prawdę ciekawie. Na powyższym przykładzie na początku widać jedynie tekst wyświetlany z niechcianym przez nas pogrubieniem. Dodałem button „create fix div”, kliknięcie którego tworzy w drzewie DOM element z klasą, która zawiera parametr:

-webkit-backface-visibility: hidden;

Po utworzeniu w drzewie DOM elementu, który ma określony parametr backface-visibility nie tylko powoduje „naprawienie” jego samego, ale też elementu, do którego tego parametru nie stosujemy. Dziwne, prawda? Z mojej obserwacji wynika, że pojawienie się w drzewie DOM elementu z określonymi parametrami dotyczącymi przekształceń 3d powoduje przełączenie silnika renderującego z trybu „płaskiej” przestrzeni 2d w tryb 3d. Wraz z przejściem w tryb 3d Chrome zmienia sposób wyświetlania czcionki, dzięki czemu tracimy wkurzające pogrubienie.

Brnąc dalej – okazuje się, że wystarczy że dla dowolnego elementu w dokumencie zastosujemy transformację 3d, chromowy WebKit przełączy się w inny tryb pracy i kłopoty z głowy. Przykład: http://jsfiddle.net/przyb/bgeHP/

I jeszcze zrzuty ekranu dla osób, którym nie bardzo chce się oglądać fiddli na Chromie, bądź dla czytających ten artykuł w momencie, kiedy w Chrome problem przestanie się pojawiać. Przed:

Po:

Jak widać na przykładzie, zastosowanie na dowolnym elemencie choćby rotateX naprawia błąd.

.absolutely-any-existing-element { -webkit-transform: rotateY(0deg); }

Dziękując za uwagę, polecam wszystkim: kiedy w przeglądarce dzieje się coś kompletnie niezrozumiałego – kombinujcie :)