Рассмотрим создание эффекта ухода текста в прозрачность как альтернативу обрезания текста многоточием.
Наверняка вы замечали, а может, и вовсе использовали на практике такой прием, как обрезание длинных слов многоточием, дабы те вписывались в дизайн.
Частый кейс ограничения длины текста — это имя пользователя. При этом не всегда под корень вырезают буквы, выходящие за допустимые рамки. Пользователь может каким-либо образом увидеть имя целиком, например, на наведение мыши ему показывается всплывающая подсказка с полным именем.
Но многоточие — не единственный способ решения. К примеру, нам в команде понравился вариант с уходом длинных имен плавно в прозрачность (Рис. 1)[1].
Рис. 1
Способы его реализации далее и рассмотрим. В качестве примера будем использовать содержимое рисунка выше (Рис. 1). Будем все описывать, используя HTML и CSS. Без React’а и webpack’а, простите.
Решение 1. CSS (функция «linear-gradient»)
Первое что может прийти в голову — использовать CSS функцию linear-gradient
.
Описываем прямоугольник:
- высота равна кеглю;
- ширина равна N (N зависит от того, насколько мы хотим покрыть градиентом участок);
- в зависимости от цвета фона задаем градиент (одна из точек полностью прозрачная, другая сплошная);
- абсолютным позиционированием закрепляем к правому краю блока с текстом.
Используя этот алгоритм, воссоздадим наш пример. Напоминаю, у нас бирюзовый текст на белом фоне.
Разметка:
<span class="text-eclipse" aria-label="Johnny Smith" title="Johnny Smith">Johnny Sm</span>
Стилизация:
.text-eclipse {
position: relative;
}
.text-eclipse::after {
content: "";
position: absolute;
top: 0;
right: 0;
width: 45%; /* 1 */
height: 100%;
background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgb(255, 255, 255) 87%); /* 2 */
}
- ширину лучше зададим в относительных единицах, чтобы не делать лишних операций при изменении размеров шрифта;
- по стандарту
transparent
— это на самом деле сокращение отrgba(0, 0, 0, 0)
[3]. В связи с этим в Safari наблюдается баг [4].
В итоге мы получаем наш результат (Рис. 1).
Но что будет, если мы перекрасим фон? (Рис. 2)
Рис. 2
В этом случае нам нужно не забыть переопределить и градиент нашего прямоугольника, перекрывающий текст (Рис. 3).
Рис. 3
Это был сплошной фон. Опустим вероятность того, что он меняется динамически, но то, что фон может быть задан как градиент, мы не должны исключать (Рис. 4).
Рис. 4
Здесь нам уже сложно подобрать цвета к градиенту нашего прямоугольника. Я не мог закрыть глаза на эту проблему, так как в нашем дизайне фон в некоторых местах перетекал в градиент (радиальный, если быть точным).
Итак, минусы данного способа:
- цвет градиента завязан на цвете фона, и об нужно знать и помнить;
- если для фона применяется градиент, то мы ничего не сможем сделать.
Решение 1.1. CSS (свойство «background-clip»)
В CSS есть свойство background-clip
(на момент написания статьи входит в статус CR[2] в спецификации), который по стандарту принимает три значения border-box
, padding-box
и content-box
. Если добавить к свойству префикс -webkit-
, появится ещё одно — text
(Рис. 5). Как раз оно нам и нужно.
Рис. 5
На рисунке (Рис. 5) наглядно видно, как работает каждое значение. Важно, что в примере с text
также задается прозрачный цвет шрифта, иначе применение свойства потеряло бы свой смысл, т.к. мы вовсе не увидели бы, как обрезается фон.
Благодаря такому поведению фона при background-clip
можно решить нашу задачу. Задаем через градиент цвет шрифта, используя background
(Рис. 6).
Разметка:
<span class="text-eclipse" aria-label="Johnny Smith" title="Johnny Smith">Johnny Sm</span>
Стилизация:
.text-eclipse {
background: linear-gradient(to right, rgb(0, 186, 187) 50%, rgba(0, 186, 187, 0) 95%);
-webkit-background-clip: text;
color: transparent;
}
Рис. 6
Теперь мы никак не зависим от цвета фона.
Но, естественно, не бывает все «идеально». Есть как минимум два минуса:
- проблематично менять цвет текста из-за использования
linear-gradient
. Хотелось бы, чтобы он наследовался и не заботил нас; - если нужна поддержка IE и Edge, то значение
text
уbackground-clip
не удовлетворяет этому условию, из-за чего будет просто закрашенная фигура (Рис. 7).
Рис. 7
Решение 2. SVG
До того, как желание наконец-таки написать супербиблиотеку на Javascript, которая будет решать эту тривиальную задачу, одержит верх, стоит вспомнить про всеми любимый и гибкий SVG. Его возможности не ограничены созданием лишь примитивных фигур и кривых. Конкретно нас интересует элемент <text>
.
Заранее отметим следующее:
- элементы SVG никак не определяют его размеры, за это отвечают
width
,height
иviewBox
(еслиwidth
иheight
не объявлены). То есть в нашем случае каким бы длинным ни был текст, он не повлияет на родителя; - для стилизации любых SVG-элементов есть два основных атрибута:
fill
(цвет заливки) иstroke
(цвет обводки). Если проводить аналогию с CSS, то первый — это сразу иbackgound
, иcolor
, а второй —border-color
. Получается<text>
мы можем спокойно залить градиентом с помощьюfill
.
Опишем для начала градиент:
<linearGradient>
<stop offset="0.5" stop-color="#00babb" />
<stop offset="0.95" stop-color="#00babb" stop-opacity="0" />
</linearGradient>
Это эквивалентно записи linear-gradient(to right, rgb(0, 186, 187) 50%, rgba(0, 186, 187, 0) 95%)
.
Применим градиент к тексту:
<svg xmlns="http://www.w3.org/2000/svg">
<linearGradient id="textEclipseGradientId">
<stop offset="0.5" stop-color="#00babb" />
<stop offset="0.95" stop-color="#00babb" stop-opacity="0" />
</linearGradient>
<text fill="url(#textEclipseGradientId)">Johnny Sm</text>
</svg>
В итоге получаем (Рис. 8):
Рис. 8
Хм, что-то пошло не так. Давайте разберёмся.
С размерами все ясно: мы их не задали, поэтому применились размеры по умолчанию (300x150). Тогда получается проблема с позиционированием текста.
Для вертикального позиционирования существует атрибут y
. Зададим ему половину высоты SVG. Как и с методом вертикального позиционирования блоков через top: 50%
нам нужно сделать что-то наподобие transform: translateY(-50%)
. Нам поможет атрибут dy
у элемента <text>
. Будем задавать относительной единицей. Это примерно 0.34em
от размера шрифта (Рис. 9).
Разметка:
<svg xmlns="http://www.w3.org/2000/svg">
<linearGradient id="textEclipseGradientId">
<stop offset="0.5" stop-color="#00babb" />
<stop offset="0.95" stop-color="#00babb" stop-opacity="0" />
</linearGradient>
<text y="50%" dy="0.36em" fill="url(#textEclipseGradientId)">Johnny Sm</text>
</svg>
Рис. 9
Теперь разберемся с размерами. Раз элементы не могут менять размеры SVG, то создадим HTML-элемент с тем же текстом, а SVG будем позиционировать абсолютно относительно него.
Разметка:
<span class="text-eclipse">
<svg xmlns="http://www.w3.org/2000/svg">
<linearGradient id="textEclipseGradientId">
<stop offset="0.5" stop-color="#00babb" />
<stop offset="0.95" stop-color="#00babb" stop-opacity="0" />
</linearGradient>
<text y="50%" dy="0.34em" fill="url(#textEclipseGradientId)">Johnny Sm</text>
</svg>
<span>Johnny Sm</span>
</span>
Стилизация:
.text-eclipse {
position: relative;
line-height: 1;
}
.text-eclipse svg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.text-eclipse svg + span {
color: transparent;
}
Сейчас цвет зашит в элементе <stop>
, а нам бы хотелось, чтобы он наследовался от color
. Для этого нам нужно задать color: inherit
и stop-color: currentColor
для <svg>
и stop-color: inherit
для <stop>
. Но тут не все так просто. stop-color: inherit
ведет себя как background: inherit
, поэтому нужно применить его и к элементу <linearGradient>
.
Стилизация:
.text-eclipse svg {
color: inherit;
stop-color: currentColor;
}
.text-eclipse linearGradient,
.text-eclipse stop {
stop-color: inherit;
}
В принципе все. Давайте отшлифуем некоторые моменты и добавим ARIA-атрибуты.
Разметка:
<span class="text-eclipse">
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="0" height="0">
<linearGradient id="textEclipseGradientId">
<stop offset="0.5" />
<stop offset="0.95" stop-opacity="0" />
</linearGradient>
<text y="50%" dy="0.34em" fill="url(#textEclipseGradientId)">Johnny Sm</text>
</svg>
<span aria-label="Johnny Smith" title="Johnny Smith">Johnny Sm</span>
</span>
Note:
- на всякий случай задаем
width="0"
иheight="0"
, чтобы страница не прыгала до применения CSS; aria-hidden="true"
скрываем от экранных читалок;
Стилизация:
.text-eclipse {
position: relative;
line-height: 1;
}
.text-eclipse svg {
position: absolute;
top: 0;
left: 0;
z-index: 0;
width: 100%;
height: 100%;
color: inherit;
stop-color: currentColor;
}
.text-eclipse:hover svg {
color: inherit; /* 1 */
}
.text-eclipse linearGradient,
.text-eclipse stop {
stop-color: inherit;
}
.text-eclipse svg + span {
position: relative;
z-index: 5; /* 2 */
color: transparent;
}
- хак, который принуждает
<linearGradient>
инициализироваться заново, чтобы сработала смена цвета; - делаем HTML-элемент главным в потоке, так как SVG выступает лишь в качестве «маски».
И тут не без проблем:
- значение
dy
для вертикального выравнивания строго зависит от используемого семейства шрифта; - в зависимости от шрифта «хвост» первой буквы в слове может обрезаться (можно указать
overflow: visible
у<svg>
, но это не сработает в IE и Safari); - так как в SVG у масок, градиентов, паттернов и т.п. должен быть уникальный id для их применения, для
<linearGradient>
следует генерировать новый id во избежание конфликтов между несколькими такими элементами. Вынести в один градиент не получится, так мы наследуем цвет от конкретного<svg>
.
Если говорить про кроссбраузерность, то должно работать везде, где поддерживается SVG 1.0.
Итого
Из рассмотренных решений я, конечно же, отдаю свое предпочтение варианту с SVG, так как он намного гибче, чем остальные. Нас совсем не заботит ни цвет фона, ни градиент. Мы работаем с обычным текстом, а SVG подстраивается.
Вообще, задача действительно простая, нужна только внимательность и хорошая осведомленность, чтобы решить её быстро.
Если у вас есть замечания или предложения, то прошу поделиться ими в комментариях к статье.
Спасибо.
Примечание
- В примере мы имеем ограничение в 9 символов. Задача, где срезать лишние символы — на сервере или на клиенте, сугубо индивидуальная.
- CR — Candidate Recommendation.
- CSS Image Values and Replaced Content Module Level 3; 4.4 Gradient Color-Stops
- Баг с transparent в Safari
Автор: Мирджамолов Иномджон