Ограничение длины текста через градиент
Рассмотрим создание эффекта ухода текста в прозрачность как альтернативу обрезания текста многоточием.
Наверняка вы замечали, а может, и вовсе использовали на практике такой прием, как обрезание длинных слов многоточием, дабы те вписывались в дизайн.
Частый кейс ограничения длины текста — это имя пользователя. При этом не всегда под корень вырезают буквы, выходящие за допустимые рамки. Пользователь может каким-либо образом увидеть имя целиком, например, на наведение мыши ему показывается всплывающая подсказка с полным именем.
Но многоточие — не единственный способ решения. К примеру, нам в команде понравился вариант с уходом длинных имен плавно в прозрачность (Рис. 1)[1].
Рис. 1
Способы его реализации далее и рассмотрим. В качестве примера будем использовать содержимое рисунка выше (Рис. 1). Будем все описывать, используя HTML и CSS. Без React«а и webpack«а, простите.
Решение 1. CSS (функция «linear-gradient»)
Первое что может прийти в голову — использовать CSS функцию linear-gradient
.
Описываем прямоугольник:
- высота равна кеглю;
- ширина равна N (N зависит от того, насколько мы хотим покрыть градиентом участок);
- в зависимости от цвета фона задаем градиент (одна из точек полностью прозрачная, другая сплошная);
- абсолютным позиционированием закрепляем к правому краю блока с текстом.
Используя этот алгоритм, воссоздадим наш пример. Напоминаю, у нас бирюзовый текст на белом фоне.
Разметка:
Johnny Sm
Стилизация:
.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).
Разметка:
Johnny Sm
Стилизация:
.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. Его возможности не ограничены созданием лишь примитивных фигур и кривых. Конкретно нас интересует элемент
.
Заранее отметим следующее:
- элементы SVG никак не определяют его размеры, за это отвечают
width
,height
иviewBox
(еслиwidth
иheight
не объявлены). То есть в нашем случае каким бы длинным ни был текст, он не повлияет на родителя; - для стилизации любых SVG-элементов есть два основных атрибута:
fill
(цвет заливки) иstroke
(цвет обводки). Если проводить аналогию с CSS, то первый — это сразу иbackgound
, иcolor
, а второй —border-color
. Получается
мы можем спокойно залить градиентом с помощьюfill
.
Опишем для начала градиент:
Это эквивалентно записи linear-gradient(to right, rgb(0, 186, 187) 50%, rgba(0, 186, 187, 0) 95%)
.
Применим градиент к тексту:
В итоге получаем (Рис. 8):
Рис. 8
Хм, что-то пошло не так. Давайте разберёмся.
С размерами все ясно: мы их не задали, поэтому применились размеры по умолчанию (300×150). Тогда получается проблема с позиционированием текста.
Для вертикального позиционирования существует атрибут y
. Зададим ему половину высоты SVG. Как и с методом вертикального позиционирования блоков через top: 50%
нам нужно сделать что-то наподобие transform: translateY(-50%)
. Нам поможет атрибут dy
у элемента
. Будем задавать относительной единицей. Это примерно 0.34em
от размера шрифта (Рис. 9).
Разметка:
Рис. 9
Теперь разберемся с размерами. Раз элементы не могут менять размеры SVG, то создадим HTML-элемент с тем же текстом, а SVG будем позиционировать абсолютно относительно него.
Разметка:
Johnny Sm
Стилизация:
.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;
}
Сейчас цвет зашит в элементе
, а нам бы хотелось, чтобы он наследовался от color
. Для этого нам нужно задать color: inherit
и stop-color: currentColor
для и
stop-color: inherit
для
. Но тут не все так просто. stop-color: inherit
ведет себя как background: inherit
, поэтому нужно применить его и к элементу
.
Стилизация:
.text-eclipse svg {
color: inherit;
stop-color: currentColor;
}
.text-eclipse linearGradient,
.text-eclipse stop {
stop-color: inherit;
}
В принципе все. Давайте отшлифуем некоторые моменты и добавим ARIA-атрибуты.
Разметка:
Johnny Sm
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;
}
- хак, который принуждает
инициализироваться заново, чтобы сработала смена цвета; - делаем HTML-элемент главным в потоке, так как SVG выступает лишь в качестве «маски».
И тут не без проблем:
- значение
dy
для вертикального выравнивания строго зависит от используемого семейства шрифта; - в зависимости от шрифта «хвост» первой буквы в слове может обрезаться (можно указать
overflow: visible
у, но это не сработает в IE и Safari);
- так как в SVG у масок, градиентов, паттернов и т.п. должен быть уникальный id для их применения, для
следует генерировать новый id во избежание конфликтов между несколькими такими элементами. Вынести в один градиент не получится, так мы наследуем цвет от конкретного.
Если говорить про кроссбраузерность, то должно работать везде, где поддерживается 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