Игры на чистом CSS со свойством counter-increment
Перевод статьи Pure CSS Games with Counter-Increment с сайта una.im, опубликовано на css-live.ru с разрешения автора — Юны Кравец.
Обожаю 1) умные хаки и 2) раздвигать границы CSS. Многие недооценивают его мощь (особенно в сочетании с мастерством препроцессинга Sass). В этой статье сочетаются обе эти тяги. Даже если это не понадобится в очередном проекте для клиента, однозначно стоит экспериментировать и раздвигать границы, чтобы как следует понять сам язык. Вы тоже можете делать игры на чистом CSS!
Counter-Increment
counter-increment
– довольно малоизвестное CSS-свойство, изначально предназначенное для нумерования оглавлений. оно весьма практично и при этом отлично поддерживается:
На Codrops есть отличный CSS-справочник, содержащий это свойство. Рекомендую в него заглянуть.
Counter-Reset
Чтобы использовать counter-increment
, сначала нужно инициализировать счётчик в разделе с помощью counter-reset
. Значения для counter-reset
и counter-increment
должны совпадать, но могут быть любыми. Например:
// у родительского элемента есть counter-reset // применённый для его инициализации section { counter-reset: unicornCounter; } // указываем, какие из дочерних элементов будут подсчитываться section h1 { counter-increment: unicornCounter; }
В коде выше каждый следующий элемент <h1>
получит номер на 1
больше (это число можно изменить, как мы узнаем немного позже).
Подсчёт с помощью counter-increment
У CSS не так уж много (пока) вариантов или способов для хранения чисел, но есть небольшая лазейка: флажки (или <input type="checkbox">
). У флажков есть глобальный атрибут checked
, который можно изменять без JavaScript (а просто по клику). Это делает его уникальным и подходящим для динамического подсчёта.
input:checked { counter-increment: counterName; }
Можно установить counter-increment
для каждого элемента input, у которого есть глобальный атрибут checked
(показанный выше), чтобы посчитать количество отмеченных чекбоксов. С помощью простых кликов пользователь динамически добавляет и удаляет чекбоксы из этой группы, а counter-increment
реагирует соответственно.
Отображение количества
Для отображения счётчика можно использовать псевдоэлемент и свойство content
! Поскольку название нашего счётчика — переменная, которая хранит значение counter-increment
, можно обратиться к значению инкремента в CSS вот так:
span::after { content: counter(counterName); }
Примечание: элемент для вывода значения счётчика должен идти после считаемых элементов, в порядке DOM.
Вот пример игры, которую я создала при помощи counter-increment
, чтобы посчитать сумму поражённых мишений. Каждая мишень — поле чекбокса со случайной длительностью анимации, которая применена к ней для изменения скорости её движения. Также я использовала CSS-анимацию с 10-и секундной задержкой, чтобы остановить игру, наложив по таймеру слой поверх страницы. Чтобы остаться в этом окне, рекомендую «перезапустить» пример на CodePen (нижний правый угол) вместо нажатия на «Играть снова».
See the Pen Pure CSS (Sass) Carnival Game by Максим (@psywalker) on CodePen.
Все эти анимации сделаны при помощи простых CSS-переходов и CSS-анимаций, но Web Animations API в ближайшем будущем обещает намного больше возможностей управления.
Пользовательские счётчики
Можно также установить пользовательские счётчики (множественное число не ошибка, поскольку можно использовать несколько счётчиков) и указать шаг счетчика (который по умолчанию равен 1
). Синтаксис будет таким:
// Нумеровать поля с шагом 2 input:checked { counter-increment: counterName 2; } // вычесть 3 с каждым отмеченным флажком input:checked { counter-increment: counterName -3; }
Поэтому, чтобы немного усложнить игру выше, можно взять уникальный счётчик для каждого кольца мишени:
See the Pen Multi-Point Target by Максим (@psywalker) on CodePen.
В случае выше, каждая мишень представляет собой не отдельный флажок, а список из трёх полей:
<!-- HTML для списка мишеней --> <ul class="game-area"> <!-- Отдельная мишень --> <ul class="target"> <li><input type="checkbox" class="inner-check"></li> <li><input type="checkbox" class="middle-check"></li> <li><input type="checkbox" class="outer-check"></li> </ul> <!-- Следующие мишени тут --> </ul>
И каждый из них ссылается на один счётчик (game), но применяет разные значения counter-increment
:
.inner-check { @extend %target-ring; // ... стиль для цвета и позиции z-index: 3; // Приращение «game» на 5 &:checked { counter-increment: game 5; } &:checked::after { content: '+5'; } } .middle-check { @extend %target-ring; // ... стиль для цвета и позиции z-index: 2; // Приращение «game» на 3 &:checked { counter-increment: game 3; } &:checked::after { content: '+3'; } } .outer-check { @extend %target-ring; // ... стиль для цвета и позиции z-index: 1; // Приращение «game» на 1 &:checked { counter-increment: game 1; } &:checked::after { content: '+1'; } }
Логика на флажках
Теперь можно делать по-настоящему сумашедшие вещи, написав «логику на флажках» — некоторый код для проверки определённых паттернов. Для этого возьмём код, основанный на порядке чекбоксов с помощью псевдоселекторов :checked
и :not(:checked)
.
Например, :checked + :checked
выберет элемент с отмеченным чекбоксом, рядом с которым находится другой элемент с отмеченным чекбоксом. :checked + :not(:checked) + :checked
выберет паттерн «отмечен, не отмечен, отмечен»
Я использовала этот подход в CSS-логике (вместе с некоторыми анимационными трюками, упомянутыми выше, на этот раз с z-index для очерёдности ходов), чтобы воссоздать игру «Крестики-нолики» на чистом CSS. Розовые и зеленые рамки показывают, когда чей ход, просто дожидайтесь своей очереди.
See the Pen Pure CSS Tic Tac Toe! by Максим (@psywalker) on CodePen.
Вот как выглядит логика «Крестиков-ноликов» для каждого случая, где Х должен выиграть:
// x | x | x // | | // | | // // примечание: также работает для горизонтального совпадения // 2 и 3 рядов, но понадобится больше логики, // чтобы избежать ложных срабатываний из 3-х последовательных проверок. :checked + :checked + :checked ~ span::after { ... } // x | | // x | | // x | | :checked + :not(:checked) + :not(:checked) + :checked + :not(:checked) + :not(:checked) + :checked ~ span::after { ... } // | x | // | x | // | x | :not(:checked) + :checked + :not(:checked) + :not(:checked) + :checked + :not(:checked) + :not(:checked) + :checked ~ span::after { ... } // | | x // | | x // | | x :not(:checked) + :checked + :not(:checked) + :not(:checked) + :checked + :not(:checked) + :not(:checked) + :checked ~ span::after { ... } // | | x // | x | // x | | :not(:checked) + :not(:checked) + :checked + :not(:checked) + :checked + :not(:checked) + :checked ~ span::after { ... } // x | | // | x | // | | x :checked + :not(:checked) + :not(:checked) + :not(:checked) + :checked + :not(:checked) + :not(:checked) + :not(:checked)+ :checked ~ span::after { ... }
Сложность может быстро возрасти, но Sass выручит. С помощью этого метода Джейк Олбо сделал невероятное демо двоичного калькулятора, демонстрирующее мощь метода и огромную пользу Sass. Когда логика станет сложнее, вам потребуется программистское искусство, чтобы справиться с поддерживаемостью кода:
See the Pen CSS: Binary Writer by Jake Albaugh (@jakealbaugh) on CodePen.
P.S. Это тоже может быть интересно:
«Значения для counter-reset и counter-reset должны совпадать» видимо «counter-increment»
Да, вы правы. Спасибо, поправил!