Игра на чистом CSS, без грязной HTML-разметки
Примерами «чего-либо на чистом CSS» сегодня удивить трудно, но обычно это значит лишь «без скриптов», т.е. с хаками в разметке, типа вставленных в строго определенные места невидимых чекбоксов. Отчего разметка становится нелогичной и негибкой. Так что «чистый CSS» — это почти всегда «грязный HTML». Хейдон Пикеринг тоже настолько устал от этого, что высказался в твиттере:
Если вы заявляете, что сделали что-то на «чистом CSS», я даже видеть не хочу HTML. Удачи.
Многие читатели прицепились к фразе «видеть не хочу HTML» буквально. Действительно, хотя CSS-оформление привязано к DOM-элементам, CSS может работать и с минимумом HTML-разметки. А то и вообще без нее! Сразу несколько человек вспомнили старинный пример Матиаса Байненса. Он использует псевдоэлемент для элемента body
(для которого необязательны и закрывающий, и открывающий теги, т.е. он присутствует в DOM независимо от наличия соотв. тегов в разметке!) плюс малоизвестный (и поддерживаемый пока только в Firefox и старой Опере) HTTP-заголовок Link
, позволяющий браузеру ссылаться на файл стилей без тега <link>
. Но сами комментаторы были вынуждены признать, что такой подход слишком ограничен, чтобы быть полезным.
Но кто ж готов мириться с ограничениями, тем более вечером в пятницу! Уж точно не ваш покорный слуга:). Вызов принят: что если не полезное, то по крайней мере интересное/занятное можно сделать в наши дни на чистом CSS, по возможности (в идеале — вообще) не используя разметку?
Итогом моих небольших экспериментов стал нехитрый набросок «ремейка» популярной несколько лет назад игры на реакцию, в которой надо как можно дольше удерживать курсор в игровом поле, избегая «столкновения» с летающими объектами. Время, в течение которого игроку это удавалось, фиксируется секундомером. Вот он:
See the Pen Игра на чистом CSS без JS и HTML-разметки (проверка идеи) by Ilya Streltsyn (@SelenIT) on CodePen.
Чтобы начать игру заново, нужно нажать «Rerun» (а в отдельном примере — обновить страницу).
Использована только стандартная базовая разметка от CodePen. Но та же логика будет работать и без нее. Для работы в Chrome достаточно одного лишь тега <link>
, а в Firefox даже он не нужен — его можно заменить HTTP-заголовком. Не верите — убедитесь сами! А заодно оцените такую диковинку, как псевдоэлемент в псевдоэлементе:)
Какие «секретные ингредиенты» тут использованы
- Элементы
html
,head
иbody
, которые всегда есть в DOM, даже если в разметке нет соотв. тегов. Справедливости ради, как отметил и Хейдон, это всё-таки HTML-элементы — но без HTML-тегов в разметке мы обошлись. - По паре псевдоэлементов
::before
и::after
для каждого из них. Как раз хватило на 4 «летающих блока», секундомер и маленький текстовый заголовок. - Свойство
pointer-events
. Оно пришло из SVG, поэтому поддерживается без префикса, но современные браузеры уверенно применяют его и в HTML (правда, со стандартизацией у него есть проблемы, из CSS UI 3-го уровня его выбросили, а в 4-й пока не перенесли… но главное, что работает:). Позволило сделать так, чтобы на наведение курсора реагировали только псевдоэлементы, но не сами элементы. - Обычные анимации. Для всех 4 «летающих блоков» в примере использована одна и та же анимация, но с разными периодами. Периоды соотносятся как простые числа, поэтому картина в целом выглядит достаточно хаотичной. Но можно сделать анимацию разной и более сложной. В теории, можно было бы анимировать как сами
head
иbody
, так и их псевдоэлементы, чтобы получить еще более хаотичное движение — но не стоит, потому что это потребует перерисовки больших площадей, а значит, может тормозить. - Пошаговая (steps) анимация с единицей
ch
. Основа «секундомера». Идея почерпнута из старого примера Лии Веру. - Один из псевдоэлементов приходится использовать в двух качествах сразу — как «летающий блок» и как второй (остановленный) секундомер. И использовать на нем две анимации одновременно (вторую — через наследуемое от родителя свойство). Был бы в CSS родительский селектор (или псевдокласс-функция
:has()
, как в новых редакциях селекторов 4 уровня), без этого удалось бы обойтись. - Сохранение состояния с помощью очень большой задержки при
transition
(с оговорками, см. ниже).
Что так и не получилось
- При уводе курсора с поля после окончания игры Chrome и Firefox ведут себя по-разному: первый возобновляет анимацию (отчего секундомер становится похож на скринсейвер времен Windows 95), второй нет. Видимо, дело во взаимодействии анимаций с
transition
. Интересно, кто из браузеров прав? - При анимации сдвига текста Firefox оказался крайне чувствительным к малейшим неточностям размера шрифта: если задать шрифт не точно в пикселях, а в
vmin
или даже вem
, из-за накопления ошибки округления текст постепенно смещался и начинала вылезать следующая цифра. - IE11 отказался анимировать смещения в единицах
vmin
, испортил анимацию секундомера (к сожалению, похоже, единицаch
там просто сломана) и, что непонятнее и неприятнее всего, не соблюдает задержку перехода при уводе курсора с поля. Первые две проблемы я почти смог побороть в отдельной версии (хотя анимация в IE там всё равно выглядит странно), последнюю так и не смог. Может быть, у вас получится лучше? - При уводе курсора с игрового поля секундомер сбрасывается на 0. Хотя, возможно, это не такой уж баг — ведь такое «бегство» можно расценить как то, что игрок сдался:)
Конечно же, это проверка идеи, сделанная исключительно ради интереса и удовольствия… а также демонстрации возможностей соврменного CSS. Как видите, время, когда чистый CSS сможет быть полезным сам по себе, без необходимости в грязном HTML, возможно, уже не за горами!
P.S. Это тоже может быть интересно:
Баг. Если навести курсор на поле, после чего убрать с мыши руку(оставить ее без движения) квадраты попадают на область курсора, и игра продолжается.
Признаю. Причем в Хроме стабильно, в Фоксе как-то через раз. Надо будет дополнительно поисследовать поведение :hover с анимируемыми элементами. Есть одна задумка, как это можно бы поправить, ближе к концу дня постараюсь проверить. Спасибо за багрепорт!
;-)
Это не баг, а фича
Не то, чтобы баг, пожалуй, больше недоработка. В левом нижнем углу, над таймером, «мёртвая зона». Можно там курсор оставить и он будет в целости и сохранности, но это уже конечно больше оффтоп, т.к. основной вопрос не в этом. Отличный результат!
интересное хаотичное движение кубиков? на протяжение 5 минут курсор находился в районе часов а именно «позиция часы (около минут :)) не один кубик туда не приполз стало скучно!!!
Движение, конечно, псевдослучайное, это была быстрая проверка возможности, поэтому такая недоработка имеет место. Но это ограничение не самого «чистого CSS», а всего лишь лени автора примера)