В этом посте рассматривается интерактивность CSS, показаны некоторые приемы, которые можно использовать для создания простого редактора аватаров без какого-либо JavaScript. Это было забавно реализовать, и я надеюсь, что будет также забавно прочитать & попробовать. При этом я бы не рекомендовал никому использовать это в производстве. 😊
Интерактивность с помощью CSS
Селектор псевдокласса CSS :checked может быть использован для добавления базового уровня интерактивности на страницу без использования JavaScript. Это специальный селектор, который включается и выключается, когда пользователь переключает флажок или радиокнопку. Например, в следующем фрагменте отмеченный флажок становится в два раза больше, чем не отмеченный:
input:checked {
transform : scale(2);
}
Что выглядит следующим образом:
Привязав метку к флажку или радиокнопке (используя id
и for
), вы также можете нажать на метку, чтобы переключить флажок:
<input type="checkbox" id="box" />
<label for="box">Click label to toggle</label>
Используя комбинаторы CSS sibling, вы можете воздействовать на метку при переключении флажка:
input:checked + label {
background : orangered;
}
Если вы затем скроете флажок, его все равно можно будет переключить с помощью метки. Это открывает интересные возможности, как в этих примерах с использованием только CSS, найденных на CodePen:
- Tabs
- меню гамбургера
- Игра с укладкой блоков
Построение элемента управления диапазоном только на CSS
Редактор аватара, показанный вверху, использует набор элементов управления диапазоном, чтобы повлиять на внешний вид аватара. Чтобы сделать это без использования JavaScript, мы не можем использовать <input type="range">
. Вместо этого мы должны создать свой собственный, используя описанные выше приемы.
Каждый сегмент диапазона будет представлен радиокнопкой и меткой. Метка будет стилизована под сегмент элемента управления диапазона. Ниже приведен HTML, определяющий элемент управления диапазоном с 4 сегментами:
<div class="range">
<input type="radio" id="range4" name="range">
<label for="range4"></label>
<input type="radio" id="range3" name="range">
<label for="range3"></label>
<input type="radio" id="range2" name="range" checked>
<label for="range2"></label>
<input type="radio" id="range1" name="range">
<label for="range1"></label>
</div>
Без стилей мы никого не обманем:
Добавив немного CSS, мы можем сделать его похожим на дорожку элемента управления диапазоном:
.range {
display : flex;
}
/* Hide the radio buttons */
.range input {
display : none;
}
/* Style labels to look like a solid range control track */
.range label {
width : 16px;
height : 6px;
background : #efefef;
border-block : 1px solid #b2b2b2;
display : flex;
cursor : pointer;
align-items : center;
}
.range label:first-of-type {
border-radius : 6px 0 0 6px;
border-inline-start : 1px solid #b2b2b2;
}
.range label:last-of-type {
border-radius : 0 6px 6px 0;
border-inline-end : 1px solid #b2b2b2;
}
В результате получаем следующее:
Чтобы выделить текущее значение, мы используем :checked
и стилизуем псевдоэлемент для метки, который будет выполнять роль большого пальца:
Теперь перейдем к самому сложному: мы также хотим выделить часть дорожки перед большим пальцем. Проблема в том, что не существует CSS-комбинатора (пока, во всяком случае, не существует) для выделения предыдущих братьев и сестер. Изменив порядок элементов в DOM, мы можем выделить то, что визуально кажется предыдущими братьями и сестрами с помощью комбинатора ~
😵💫.
.range {
/* Reverse element order */
flex-direction : row-reverse;
/* Move them back to the left */
justify-content : flex-end;
}
Беспорядок:
Первый сегмент теперь последний и наоборот, также нужно поменять местами селекторы :first-of-type
и :last-of-type
, чтобы внешний вид снова стал правильным:
И напоследок добавим выделение дорожки (это стало возможным благодаря инверсии, описанной выше):
.range :checked ~ label {
background : #0075ff;
border-color : #4b76bb;
}
Выглядит как элемент управления диапазоном!
Использование регулятора диапазона для интерактивности
Хотя вышеупомянутый элемент управления диапазоном выглядит красиво и является интерактивным, мы должны использовать его для манипулирования чем-то другим 🤔. Мы можем (в настоящее время) воздействовать только на родственные элементы, снова используя комбинатор ~
. Поэтому мы должны поместить еще один элемент во внешний элемент .range
.
<div class="range">
<!-- Existing range markup -->
<div class="avatar"></div>
</div>
И использовать еще немного CSS-магии, здесь для масштабирования аватара в зависимости от значения диапазона (используя переменные CSS):
/* Represent the leftmost value, since we reversed the elements */
:nth-of-type(4):checked ~ .avatar {
--avatar-size : 0.5;
}
:nth-of-type(3):checked ~ .avatar {
--avatar-size : 0.75;
}
:nth-of-type(2):checked ~ .avatar {
--avatar-size : 1;
}
/* Rightmost value */
:nth-of-type(1):checked ~ .avatar {
--avatar-size : 1.25;
}
.avatar {
/* Lots of CSS here to make the avatar look like a dog */
transform : scale(var(--avatar-size));
}
Попробуйте:
Вот и все! 🎉 Используя приведенные в посте приемы, можно создать полноценный редактор аватара, показанный сверху, обязательно посмотрите его на CodePen. Если вы уже используете подобные приемы в своем приложении, мы будем рады услышать об этом.
PS. В недалеком будущем мы сможем использовать селектор :has()
для воздействия на элемент, который не является прямым братом или сестрой (мы также сможем избежать реверсирования элементов с его помощью). Следите за будущим обновлением с его использованием ⭐.