Półprzezroczysty cień warstwy okna
28 lipca 2007

Przygotowując kawałek kodu JavaScript zechciałem wyraźnie zaznaczyć, że pływająca warstwa znajduje się nad resztą strony. Oczywistym wyborem był cień – wyzwanie polegało na tym, że chciałem aby był prawdziwie półprzezroczysty oraz wyglądał inaczej (IMO: lepiej). Od zawsze podobał mi się efekt Outer Glow w Photoshopie, stąd i taki zamierzałem zrobić w CSS.
Na początku zamierzałem podejść jak do zwykłego cienia – siatka ala tabelka, gdzie wymiary rogów pokrywają się z wysokością i szerokością cieni poziomych i pionowych. Problem w tym, że tworząc prawdziwą poświatę, obszar w którym należy zawrzeć róg jest znacznie większy niż reszta efektu.

Z tego powodu odpada rozwiązanie wykorzystujące wysokość zawartości warstwy do rozciągnięcia cieni pionowych – albo powstanie pusta przestrzeń na górze i dole albo cienie będą się nachodzić (ciemniejsze miejsca, jako że używamy PNG z kanałem alpha). Kolejny minus wynikający z takiego rozwiązania to stała szerokość warstwy (aby filtry IE działały)
Postanowiłem umiejscowić każdy fragment poświaty przez pozycjonowanie absolutne, wykorzystując fakt, że łącząc left z right oraz top z bottom, można rozciągnąć element na dowolnej przestrzeni. Jedyną przeglądarką nie potrafiącą tego zrobić jest oczywiście Internet Explorer 6, ale i tym się zajmiemy.
Struktura cienia
HTML jest dość złożony, ale zakładam że wszystkie dodatkowe elementy zostaną wygenerowane w JavaScript.
<div class="layer"><div class="content"><p>Lorem…</p></div><div class="shadow-top"><div class="left"></div><div class="center"></div><div class="right"></div></div><div class="shadow-middle"><div class="left"></div><div class="right"></div></div><div class="shadow-bottom"><div class="left"></div><div class="center"></div><div class="right"></div></div></div>
Podstawy
Mimo że nie jest to wymagane, na początek ustawiłem szerkość warstwy na 50% oraz dodałem padding równy wymiarom cieni w pionie i poziomie (demo).
.layer {margin: 20px;width: 50%;padding: 23px;position: relative;background-color: #fcc;}.layer .content {padding: 10px;background-color: #fff;}
Pozycjonowanie
Teraz wypada dodać najprostsze elementy - narożniki. Wszystkie kawałki cienia będą pozycjonowane absolutnie względem div.layer.
.layer {position: relative;}.layer .left,.layer .right,.layer .center {position: absolute;}.layer .shadow-top .left,.layer .shadow-top .right,.layer .shadow-bottom .left,.layer .shadow-bottom .right {width: 45px;height: 45px;background-repeat: no-repeat;background-position: 0 0;}
Wrzucamy obrazki (demo):
.layer .shadow-top .left { background-image: url(../tl.png); }.layer .shadow-top .right { background-image: url(../tr.png); }.layer .shadow-bottom .left { background-image: url(../bl.png); }.layer .shadow-bottom .right { background-image: url(../br.png); }
Teraz najważniejsza część. Pozostałe divy należy odsunąć z obu stron od krawędzi o szerokość narożnika, co spowoduje rozciągnięcie w poziomie (demo):
.layer .shadow-top .center,.layer .shadow-bottom .center {height: 23px;right: 45px;left: 45px;background-repeat: repeat-x;background-color: #fcc;}
I pionie (demo):
.layer .shadow-middle div {width: 23px;bottom: 45px;top: 45px;background-repeat: repeat-y;background-color: #cff;}
Pozostało jeszcze przesunąć narożniki pod .content, zwiększając temu ostatniemu z-index (demo).
Poprawki dla IE6. Yeah.
Dla Internet Explorera 6 należy natomiast zemulować rozciągnięcie pozycjonowaniem przez dynamiczne nadawanie width i height na podstawie wymiarów rodzica. Oraz oczywiście ustawić przezroczystość wszystkich PNG przez filtry.
Dodajemy arkusz stylów ie.css, usuwamy tło z wszystkich elementów.
.layer .left,.layer .right,.layer .center {background: none !important;}
Aby ustawić przezroczystość dla narożników używamy filtru z sizingMethod ustawionym na crop – w odróżnieniu od cieni poziomych i pionowych, gdzie należy wstawić scale.
.layer .shadow-top .left { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../tl.png", sizingMethod="crop"); }….layer .shadow-top .center { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../t.png", sizingMethod="scale"); }
Dokładny mechanizm tych filtrów jest opisany w podlinkowanym wcześniej artykule. Pamiętajmy też o poprawności ścieżek plików (taka sama reguła jak przy .htc) (demo).
Teraz przychodzi czas na nasze ulubione expressions. Przykładowo dla poziomych elementów:
.layer .shadow-top .center,.layer .shadow-bottom .center {height: expression(x = this.parentNode.parentNode.offsetWidth,y = parseInt(this.currentStyle.left),x - (y * 2) + 'px')}
Co tutaj się dzieje? Na początku przypisuję do zmiennych wysokość elementu .layer oraz aktualny odstęp od lewej, aby użyć ich poniżej. To samo powtarzamy dla elementów pionowych, używając offsetHeight oraz top.
Wszystko już jest prawie dobrze – oprócz momentu kiedy cień minimalnie nachodzi na narożniki. Trochę zabawy ze zmianą rozmiaru okna i tekstu (+ debugowanie via element.innerHTML gdy to pierwszy raz zobaczyłem ;)) i widać, że błąd pojawia się co 2 piksel.
Rozszerzyłem więc obliczenie dodając warunek sprawdzający czy nie ma reszty z podzielenia szerokości .layer przez 2 i wtedy odejmuję jeden dodatkowy piksel od wynikowej szerokości cienia. Wygląda to tak:
(x - ((x % 2) ? 1 : 0) - (y * 2)) + 'px'
Tę samą linijkę można dorzucić do pionowych elementów (.layer .shadow-middle div).
I w ten oto sposób mamy upragnioną warstwę z ładnym, estetycznym (także: innym niż się widuje w sieci) i rozciągliwym cieniem działającym na każdej przeglądarce (testowałem w Fx, Operze, Safari i obu IE). A ja teraz muszę wykorzystać to co stworzyłem. :)


