Xiper

Эффект отражения

Автор: Евгений Рыжков Дата публикации:

Задача

Добавить эффект отражения текстовому элементу. Главное требование — гибкость, т.е. текст может меняться, эффект соответственно подстраиваться.

пример эффекта отражения для текста

Решение-мечта

Упомяну сразу про CSS свойство, специально для этого предназначенное — -webkit-box-reflect. Пишем подобное правило:

.reflect {
	-webkit-box-reflect: below 0 -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(0.5, transparent), to(white));
}

и получаем нужный эффект. При чем данное свойство применимо как к тексту, так и к любым другим элементам, в включая изображения (<video> не пробовал). Не нужно беспокоится на каком фоне расположен элемент: отражение создается полупрозрачным. Единственный и серьезный минус данного решения — отсутствие кроссбраузерности: данное свойство поддерживают только webkit браузеры.

Решение-реальность

Чтобы добиться аналогичного результата кроссбраузерно, придется добавить атрибуты в HTML, воспользоваться псевдо элементами :after и :before, CSS правилами для трансформаций и градиентов:

<h1 title="Xiper - пример CSS отражения" class="h1-css">Xiper - пример CSS отражения</h1>
.h1-css {
	color: #000066;
	font-size: 24px;
	margin-bottom: 30px;
	position: relative; /* элемент к которому применяем эффект должен быть блочным и position: relative */
}
.h1-css:after { /* получаем копию оригинального текста в перевернутом виде */
	content: attr(title); /* оказывается в CSS и так можно - извлечь значение атрибута */
	position: absolute; /* отражение позиционируется относительно родителя */
	left: 0;
	top: 100%; /* можно управлять удаленностью отражения. 100% - отражение вплотную */
	width: 100%;
	height: 100%;
	-moz-transform: scaleY(-1); /* много правил для CSS переворота */
	-o-transform: scaleY(-1);
	-webkit-transform: scaleY(-1);
	-ms-transform: scaleY(-1);
	transform: scaleY(-1);
	z-index: 1; /* чтобы отражение можно было перекрыть */
}
.h1-css:before { /* создаем эффект затухания */
	content: "";
	display: block;
	width: 100%;
	height: 80%; /* подбирается в зависимости о того какой процент отражения должен быть виден */
	background:-moz-linear-gradient(top, rgba(255,255,255,0.7), rgba(255,255,255,1)); /* много правил для линейного градиента */
	background: -o-linear-gradient(top, rgba(255,255,255,0.7), rgba(255,255,255,1));
	background: -webkit-gradient(linear, left top, left bottom, from(rgba(255,255,255,0.7)), to(rgba(255,255,255,1)));
	position: absolute;
	left: 0;
	top: 110%; /* подбирается в зависимости от необходимого эффекта затухания */
	z-index: 2; / чтобы перекрыть перевернутый текст */
}

Атрибут в HTML хоть и очень не хочется, но приходится добавлять, т.к. правило content не способно прочитать текст элемента.

А что же IE?

С IE есть целый ряд проблем даже если не считаться с IE6:

  • CSS переворот (transform) IE вплоть до 9-й версии не поддерживает. Но зато имеется CSS фильтр для переворота flipV.
  • CSS градиенты тоже отсутствуют включая 9-ю версию, но зато есть фильтр Alpha.
  • IE7 не поддерживает псевдо элементы. Правда это можно решить с помощью expression.
  • в IE8-9 нельзя применить фильтры к пседоэлементам, т.к. последние применяются только к элементам, имеющие layout.
  • IE8-9 не поддерживает expression.

В итоге для IE7 можно написать CSS правила используя expression и фильтры которые тоже создадут эффект отражения. А вот IE8 и IE9, хотя браузеры и по новее, единственным преимуществом в данном случае перед IE7 оказалась поддержка псевдоэлементов. Ну и в данном случае с этой поддержи толку нет, т.к. нельзя к ним применить фильтры. И создать элементы в CSS тоже уже нельзя. Поэтому для них пришлось писать небольшой скрипт. Т.к. IE7 использует ту же технику, его тоже сюда включил. Меню — один из наиболее часто используемых компонентов сайта, для которого есть соблазн создать такой эффект. Поэтому его включил в этот пример:

<!--[if lte IE 9]>
<style>
.h1-after,
.menu-css {
	position:absolute;
	left:0;
	top:100%;
	z-index: 1;
	background: #fff;
	-ms-filter: "flipV progid:DXImageTransform.Microsoft.Alpha(opacity=50, style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy=70)"; /* для IE8-9 */
	filter: flipV progid:DXImageTransform.Microsoft.Alpha(opacity=50, style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy=70); /* для IE7 */
}
.h1-css:after,
.menu-css a:after {
	display: none; /* для IE8-9 прячем контент псевдоэлемента */
}
</style>
<script>
textReflect = function()
{
	// отражение для заголовка
	var el = document.getElementsByTagName("h1")[0];
	el.insertAdjacentHTML("afterBegin", "<span class=&h1-after&>"+el.firstChild.nodeValue+"</span>");
	// отражения для меню
	var menu = document.getElementsByTagName("a"),
		i,
		lenMenu = menu.length;
	for(i=0;i<lenMenu;i++) menu[i].insertAdjacentHTML("afterBegin", "<span class=&menu-after&>"+menu[i].firstChild.nodeValue+"</span>");
}
	window.onload=textReflect;
</script>
<![endif]-->

Смотрим демо пример. Проверено в:

  • IE 6-8
  • Firefox 4
  • Opera 11
  • Safari 5
  • Chrome

Недостатки

  • дополнительный атрибут в HTML;
  • раздутый CSS;
  • не подходит для неоднородного фона;
  • нет поддержки Opera 10 и IE6 (кому это нужно?);
  • Javascript для IE.

Итого

Решение вышло отдаленно напоминающее боевой вариант. Хотя в некоторых случаях может пригодится. А в целом вариант больше теоретический, нежели практический.

По теме