Игра на чистом 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. Это тоже может быть интересно:

6 Комментарии
  1. Роман

    Баг. Если навести курсор на поле, после чего убрать с мыши руку(оставить ее без движения) квадраты попадают на область курсора, и игра продолжается.

    1. SelenIT (Автор записи)

      Признаю. Причем в Хроме стабильно, в Фоксе как-то через раз. Надо будет дополнительно поисследовать поведение :hover с анимируемыми элементами. Есть одна задумка, как это можно бы поправить, ближе к концу дня постараюсь проверить. Спасибо за багрепорт!

      1. Роман
  2. Вадим

    Не то, чтобы баг, пожалуй, больше недоработка. В левом нижнем углу, над таймером, «мёртвая зона». Можно там курсор оставить и он будет в целости и сохранности, но это уже конечно больше оффтоп, т.к. основной вопрос не в этом. Отличный результат!

  3. sanyochek

    интересное хаотичное движение кубиков? на протяжение 5 минут курсор находился в районе часов а именно «позиция часы (около минут :)) не один кубик туда не приполз стало скучно!!!

    1. SelenIT (Автор записи)

      Движение, конечно, псевдослучайное, это была быстрая проверка возможности, поэтому такая недоработка имеет место. Но это ограничение не самого «чистого CSS», а всего лишь лени автора примера)

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.