Создание тени по типу «луковой шелухи»

Брайан Уильямс

Перевод: Влад Мержевич и Евгений Богомольный.

Да, «луковая шелуха». Аниматоры используют этот эффект, чтобы показать то, что обычно невозможно увидеть: кадры движения во времени. И теперь веб-дизайнеры могут использовать подобный эффект, чтобы показать другую кажущуюся невозможность: создание реалистичной тени с использованием CSS.

Особенности

Есть несколько отличий между данной методикой и методиками, предлагаемых ранее. Некоторые пользователи найдут описанную в этой статье технику более простой, другие — более стабильной, но в любом случае, наш вариант имеет больше преимуществ, таких как:

  • не требует определения ширины слоя (width);
  • позволяет модифицировать глубину тени без манипуляций с изображением;
  • одинаково отображается во всех браузерах.

Чистим луковицу

«Луковая шелуха» используется при создании анимации для наложения кадров последовательности движения друг на друга, чтобы можно было заметить небольшие различия между отдельными кадрами. Аниматор просто просматривает набор слоев, чтобы увидеть одно целое движение через композицию его частей. Непрозрачные области на одном слое могут быть замечены на прозрачном слое выше него. Мы можем сделать то же самое и с тегом <div>. Используя CSS, допустимо наложить друг на друга несколько элементов <div> подобно луковице и таким образом получить желаемую композицию. В частности, создать тень (рис. 1).

Рис. 1. Изометрическая проекция слоев в виде луковой шелухи

Рис. 1. Изометрическая проекция слоев в виде «луковой шелухи»

Три слоя, каждый из которых содержит фрагмент изображения тени, накладываясь друг на друга, позволяют получить единую тень. Просто поместите ваш объект внутри трех контейнеров <div> и каждому из них дайте название класса, которое соответствует его роли. В примере 1, в частности, используются имена wrap1, wrap2 и wrap3 (класс wrap1 соответствует внешнему слою, а wrap3 — внутреннему).

Пример 1. Структура слоев для создания тени

<div class="wrap1">
 <div class="wrap2">
  <div class="wrap3">
   <img src="object.gif" alt="Рисунок с тенью">
  </div>
 </div>
</div>

Код CSS сложнее кода HTML, но не намного. В основном, для создания тени требуется пройти три шага, которые выполняются через правила стиля.

  • Отображение тени. Следует установить каждое из трех изображений для создания тени (собственно тень и два уголка к ней) на определенный слой.
  • Отбрасывание тени. Требуется задать смещение изображений вправо и вниз на определенное расстояние.
  • Размер по объекту. Тень должна соответствовать размерам объекта.

Шаг 1. Отображение тени

Основная идея состоит в том, чтобы сопоставить каждый из трех компонентов изображения тени своему контейнеру <div> через соответствующий класс. Цель применения трех изображений состоит в том, чтобы использовать два маленьких рисунка для маскирования среза края тени и создания плавного закругления краев. Для этого один рисунок помещается в правый верхний угол тени от объекта, а второй — в левый нижний.

Чтобы один рисунок находился поверх другого и маскировал его, необходимо изменить его положение с помощью стилевого свойства z-index. На самом деле волноваться по этому поводу не стоит, т.к. вложенная структура тегов <div> работает на нас и позволяет автоматически получить нужный порядок. Мы просто назначаем класс с тенью самому нижнему тегу <div> в наборе слоев. Поскольку нижние теги <div> будут располагаться наверху, то им присвоим классы, которые отображают уголки тени (пример 2).

Пример 2. Стили для отображения рисунков с тенью

.wrap1 {
 background-image: url(images/shadow.gif);
}
.wrap2 {
 background-image: url(images/corner_bl.gif);
}
.wrap3 {
 background-image: url(images/corner_tr.gif);
}

Когда мы назначаем требуемый рисунок к нужному классу, следует установить для него позиционирование с помощью свойства background-position. Если мы оставим его значение по умолчанию, то все, что мы увидим — это повторяющийся рисунок corner_tr.gif, поскольку слой с ним находится наверху набора слоев. Помните, техника «луковой шелухи» требует прозрачности верхних слоев, чтобы мы могли бы увидеть то, что находится ниже них. Для этого следует отменить повторение фонового рисунка с помощью значения no-repeat и позиционировать рисунок в нужном месте композиции.

Здравый смысл говорит нам, что изображение с именем corner_tr.gif должно находиться в правом верхнем углу, в то время как corner_bl.gif — в левом нижнем. Но что касается основного рисунка с тенью (shadow.gif), надо ли позиционировать и его? Факт — надо. Если мы хотим, чтобы тень отбрасывалась правее и ниже любого объекта, то следует определить ее положение в классе wrap1. В противном случае рисунок заполнит элемент <div> относительно его верхнего левого угла, т.е. совсем не там, где он требуется.

Основной слой — DIV.wrap1

Внешний <div> содержит самый большой из трех компонентов тени (файл shadow.gif), который позиционирован по правому и нижнему краю (пример 3).

Пример 3. Стиль для основного слоя

.wrap1 {
  background: url(images/shadow.gif) right bottom no-repeat;
}

На рис. 2 показано содержимое слоя и требуемый графический файл.

Рис. 2. Содержимое слоя wrap1

Рис. 2. Содержимое слоя wrap1

Средний слой — DIV.wrap2

Второй <div>, вложенный внутри первого, маскирует срез левого нижнего края тени и устанавливает плавное закругление (пример 4).

Пример 4. Стиль для среднего слоя

.wrap2 {
 background: url(corner_bl.gif) left bottom no-repeat;
}

На рис. 3 показано содержимое слоя и требуемый графический файл.

Рис. 3. Содержимое слоя wrap2

Рис. 3. Содержимое слоя wrap2

Внутренний слой — DIV.wrap3

Третий <div>, вложенный внутри второго, занимается срезом верхнего правого края тени.

Пример 5. Стиль для внутреннего слоя

.wrap3 {
 background: url(corner_tr.gif) right top no-repeat;
}

На рис. 4 показано содержимое слоя и требуемый графический файл.

Рис. 4. Содержимое слоя wrap3

Рис. 4. Содержимое слоя wrap3

Шаг 2. Отбрасывание тени

Следующий шаг для CSS — смещение тени, которое создает эффект ее отбрасывания. Проще не бывает. Все что требуется — это добавить свойство padding, которое устанавливает поля справа и снизу для самого нижнего слоя. Когда padding применяется к тегу <div>, то он изменяет размеры объекта, и остальные два слоя изменяются вместе с ним. Как результат, все три компонента тени, расположенные по правой и нижней стороне этих слоев, смещаются одновременно. Теперь их можно увидеть через пустой промежуток, созданный с помощью свойства padding (пример 6).

Пример 6. Использование полей для отбрасывания тени

.wrap3 {
  padding: 0 4px 4px 0;
  background: url(corner_tr.gif) right top no-repeat;
}

Результат данного примера продемонстрирован на рис. 5.

Рис. 5. Использование смещения содержимого

Рис. 5. Использование смещения содержимого

Модификация смещения

Изменение величины смещения тени делается почти также просто, как и замена значений свойства padding для слоя wrap3. Мы говорим «почти», поскольку настройка значения padding просто перемещает тень, в то время как уголки продолжают прижиматься к краям контейнеров. Чтобы точно смоделировать смещение тени, для обоих уголков следует использовать свойство background-position.

Некоторые разработчики сказали бы, что достаточно настроить значение padding и остановиться на этом. Нет смысла усложнять себе жизнь вниманием к мелким деталям, когда большинство пользователей их просто не заметит. Другие бы привели аргумент, что срезанные уголки снижают эффект производимый тенью. Если метод технически и эстетически позволяет добиться желаемой точности, то разработчику грех не воспользоваться заложенным потенциалом. Тем не менее, каждый выбирает свой собственный путь.

Судите сами. Рис. 6 используется ниже для демонстрации двух видов тени.

Рис. 6. Исходное изображение для создания тени

Рис. 6. Исходное изображение для создания тени

Все образцы в обоих наборах используют одинаковое значение свойства background-image. Иными словами, одни и те же графические изображения применяются к каждому образцу. Различается только степень смещения тени.

В наборе 1 сдвиг тени регулируется только значением свойства padding для слоя wrap3, при этом перемещается только сама тень без уголков. В наборе 2 значения padding и background-position изменяются таким образом, чтобы одновременно смещалась тень и ее уголки.

Набор 1. Результат использования свойства padding
8 пикселов 12 пикселов 18 пикселов
Набор 2. Результат одновременного использования свойств padding и background-position
8 пикселов 12 пикселов 18 пикселов

Если вы замечаете различия между приведенными наборами и предпочитаете второй вариант, то к стилю уголков следует прибавить свойство background-position и настроить его значение таким образом, чтобы компенсировать влияние padding. Далее к изображению уголков с внешней стороны следует добавить пустое белое пространство. Это позволяет рисунку перемещаться на достаточно большое расстояние от границы слоя, без того, чтобы не терять способность маскировать край тени. Каждый вид тени немного отличается от другого, и как только вы начнете экспериментировать со смещением, то сразу станет понятно, на какую величину следует увеличить белое пространство у картинки с уголками.

Шаг 3. Размер по объекту

Потребуется небольшая ловкость рук, чтобы убедить браузер устанавливать тень по ширине объекта. Без этого потребовалось бы вычислять ширину, чтобы корректно добавить тень.

В большинстве браузеров <div> принимает размер объекта, когда для него добавляется свойство float. Данный способ подойдет, но только не в том случае, если вы используете Internet Explorer 5 под Macintosh. Простой факт, что есть люди, которые пользуются этим браузером, заставил поискать альтернативное решение. К сожалению, ни одно не было найдено, по крайней мере, такое, которое было бы универсальным.

Некоторое экспериментирование показало, что использование значения inline-table свойства display может спасти ситуацию. Так, применение комментариев совместно со слэшем (/* */display: block/**/) позволяет скрыть этот код от браузера Internet Explorer 5 под Macintosh, «подсовывая» ему строку display: inline-table, в то время как остальные браузеры «видят» свойство float: left. Таким образом, получается, что «размер по объекту» работает во всех браузерах (рис. 6).

Рис. 6. Конечный результат

Рис. 6. Конечный результат

В итоге CSS с учетом всех трех шагов показан в примере 7.

Пример 7. Окончательный стиль для создания тени

.wrap1, .wrap2, .wrap3 {
 display: inline-table;
 /* */display: block/**/
}
.wrap1 {
 float: left;
 background: url(images/shadow.gif) right bottom no-repeat;
}
.wrap2 {
 background: url(images/corner_bl.gif) left bottom no-repeat;
}
.wrap3 {
 padding: 0px 4px 4px 0px;
 background: url(images/corner_tr.gif) right top no-repeat;
}

Еще один стиль для хорошего тона

Как было показано в примерах выше, изображения достаточно часто используются с приведенной техникой. В подобном случае для рисунка, к которому добавляется тень, следует установить свойство display со значением block. Во многих случаях это поможет избежать наложения элементов друг на друга. Просто добавьте следующий стиль (пример 8).

Пример 8. Стиль для изображений

.wrap3 IMG {
 display: block;
}

Комментарии переводчика

В данной статье Брайан Уильямс изложил свою универсальную методику создания тени на основе слоев. Однако многие читатели хотели бы видеть не принцип добавления тени к объекту, а готовый код, который с легкостью можно скопировать и модифицировать «под себя». Для таких людей и написан приведенный далее текст.

Итак, вам понадобится три рисунка: собственно сама тень (рис. 7), правый верхний уголок (рис. 8) и левый нижний уголок (рис. 9).

Рис. 7

Рис. 7. Изображение тени (файл shadow.png)

Рис. 8

Рис. 8. Изображение правого верхнего уголка (файл corner_tr.png)

Рис. 9

Рис. 9. Изображение левого нижнего уголка (файл corner_bl.png)

Ширина изображения тени зависит от цели ее использования, например, если планируется устанавливать тень для рисунков шириной не более 600 пикселов, то имеет смысл сделать такой же размер и у тени.

Код для формирования тени с помощью приведенных картинок и методики «луковой шелухи» показан в примере 9.

Пример 9. Создание тени

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>Создание тени</title>
  <style type="text/css">
   .wrap1 {
    display: block; 
    background: url(images/shadow.png) no-repeat right bottom; /* Рисунок с тенью */
    float: left; /* Создание обтекания для правильного положения тени */
   }
   .wrap2 {
    display: block;
    background: url(images/corner_bl.png) no-repeat left bottom;
    background-position: -15px 100%; /* Положение уголка тени */
   }
   .wrap3 {
    display: block;
    padding: 0 12px 12px 0; /* Ширина тени справа и высота тени снизу */
    background: url(images/corner_tr.png) no-repeat right top; /* Правый верхний уголок тени */
    background-position: 100% -15px; /* Положение уголка тени */
   }
   .wrap3 IMG {
    display: block;
   }
  </style>
 </head>
 <body>
  <div class="wrap1">
   <div class="wrap2">
    <div class="wrap3"><img src="images/pic.jpg" width="72" height="72" alt="Картинка"></div>
    </div>
   </div>
 </body>
</html>

Значения, которые управляют положением тени относительно объекта, подбираются экспериментально, исходя из собственных предпочтений и дизайна сайта.

Не выкладывайте свой код напрямую в комментариях, он отображается некорректно. Воспользуйтесь сервисом cssdeck.com или jsfiddle.net, сохраните код и в комментариях дайте на него ссылку. Так и результат сразу увидят.