Эффект Glitch Лукаса Беббера выглядит очень круто — как будто вы смотрите на текст на старом мониторе, который слишком часто роняли на пол и у него «плавает» вертикальная синхронизация и сведение.
Реализация этого эффекта на CSS выглядит вполне убедительно. Мне пришлось немного поломать голову, чтобы выяснить, как он работает, и теперь я хочу объяснить это вам. Кроме того, я воспроизвёл этот эффект не только для текста, но и для растровых изображений и SVG, а так же написал несколько примесей Sass, чтобы облегчить работу с ним.
Три копии текста
HTML для этого примера выглядит просто:
<div class="glitch" data-text="GLITCH">GLITCH</div>
С помощью псевдоэлементов создаются две дополнительные копии основного элемента, которыми можно управлять индивидуально:
.glitch {
position: relative;
}
.glitch::before,
.glitch::after {
content: attr(data-text);
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
Все три копии текста расположены друг над другом:
Изменяем копии
Каждая из дополнительных копий идентична оригиналу за исключением того, что:
- они смещены влево или вправо;
- У них есть цветной ореол, созданный с помощью
text-shadow
.
Именно смещение и ореол служат основой для эффекта неисправного монитора.
.glitch::before {
/* ... всё, что нужно, чтобы сделать слой идентичным оригиналу */
/* отличия от оригинала */
left: 2px;
text-shadow: -1px 0 red;
/* Важно: непрозрачный фон закрывает предыдущий слой */
background: black;
}
.glitch::after {
/* ... всё, что нужно, чтобы сделать слой идентичным оригиналу */
/* отличия от оригинала */
left: -2px;
text-shadow: -1px 0 blue;
/* Важно: непрозрачный фон закрывает предыдущий слой */
background: black;
}
Теперь наши три копии выглядят так:
Обрезаем копии
Пока что нам видна только самая верхняя из трёх копий. Скорее всего, это версия ::after
, если только вы не меняли z-index
. Но это не важно, так как мы будем попеременно показывать части всех трёх копий с помощью свойства clip
. В данный момент это свойство уже считается устаревшим, его должно заменить clip-path
, но на момент написания статьи именно свойство clip
работало лучше. Конечно, со временем это изменится, так что нужно иногда поглядывать на ситуацию. В случае чего, их легко будет поменять, например, с помощью Autoprefixer.
У clip
довольно странный синтаксис. Требуются четыре значения, логично было бы предположить, что это координаты верхнего левого угла и длина с шириной, или координаты верхнего левого и нижнего правого углов, но вместо этого числа означают отступы, как в свойствах margin
или padding
(top/right/bottom/left
).
.glitch::before {
clip: rect(44px, 450px, 56px, 0);
/*
Получился прямоугольник с верхним левым углом 0, 44px
и нижним правым 450px, 56px
*/
}
Вот как может выглядеть результат обрезки, для наглядности фон сделан непрозрачным и слои смещены друг относительно друга:
Анимируем обрезку
Для свойства clip
можно использовать анимации CSS, меняя положение обрезающего прямоугольника. Вот пример такой анимации:
@keyframes glitch-anim {
0% {
clip: rect(70px, 450px, 76px, 0);
}
20% {
clip: rect(29px, 450px, 16px, 0);
}
40% {
clip: rect(76px, 450px, 3px, 0);
}
60% {
clip: rect(42px, 450px, 78px, 0);
}
80% {
clip: rect(15px, 450px, 13px, 0);
}
100% {
clip: rect(53px, 450px, 5px, 0);
}
}
Обратите внимание, что левый и правый край остаются неизменными, меняются только верхний и нижний край. И эти значения выбраны совершенно произвольно. С помощью Sass вполне можно генерировать их случайным образом:
@keyframes glitch-anim {
$steps: 10;
@for $i from 0 through $steps {
#{percentage($i*(1/$steps))} {
clip: rect(random(100)+px, 9999px, random(100)+px, 0);
}
}
}
Так как нам понадобятся два набора случайных обрезающих прямоугольников, надо будет сделать два набора ключевых кадров и применить их к двум копиям:
.glitch::before {
...
animation: glitch-anim-1 2s infinite linear alternate-reverse;
}
.glitch::after {
...
animation: glitch-anim-2 2s infinite linear alternate-reverse;
}
В этом месте мы можем отрегулировать скорость анимации зациклить её. Эффект готов:
Примеси Sass
Я подумал, что хорошо бы добавить этому эффекту возможность удобного повторного использования. Например, написать примесь Sass с параметрами, с помощью которых можно контролировать эффект:
.example-one {
font-size: 100px;
@include textGlitch("example-one", 17, white, black, red, blue, 450, 115);
}
Вот что у меня получилось:
/*
(TEXT) PARAMS
=================
1. Namespace
2. Intensity
3. Text color
4. Background color (flat)
5. Highlight #1 color
6. Highlight #2 color
7. Width (px)
8. Height (px)
*/
@mixin textGlitch($name, $intensity, $textColor, $background, $highlightColor1, $highlightColor2, $width, $height) {
color: $textColor;
position: relative;
$steps: $intensity;
// Ensure the @keyframes are generated at the root level
@at-root {
// We need two different ones
@for $i from 1 through 2 {
@keyframes #{$name}-anim-#{$i} {
@for $i from 0 through $steps {
#{percentage($i*(1/$steps))} {
clip: rect(
random($height)+px,
$width+px,
random($height)+px,
0
);
}
}
}
}
}
&:before,
&:after {
content: attr(data-text);
position: absolute;
top: 0;
left: 0;
width: 100%;
background: $background;
clip: rect(0, 0, 0, 0);
}
&:after {
left: 2px;
text-shadow: -1px 0 $highlightColor1;
animation: #{$name}-anim-1 2s infinite linear alternate-reverse;
}
&:before {
left: -2px;
text-shadow: 2px 0 $highlightColor2;
animation: #{$name}-anim-2 3s infinite linear alternate-reverse;
}
}
Конечно, можно придумать ещё миллион способов, как реализовать эту примесь — всё зависит от того, какие параметры вы хотите настраивать, какая у вас разметка HTML и т.д.
Я также написал ещё две примеси, одну для растровых изображений, вторую — для SVG. Они отличаются деталями реализации — там не используются псевдоэлементы, смещение и цветной ореол тоже делаются по-другому и т.д. Вот все три примеси в одном файле. А вот так выглядит их работа: