Игры на чистом CSS со свойством counter-increment

Перевод статьи Pure CSS Games with Counter-Increment с сайта una.im, опубликовано на css-live.ru с разрешения автора — Юны Кравец.

Обожаю 1) умные хаки и 2) раздвигать границы CSS. Многие недооценивают его мощь (особенно в сочетании с мастерством препроцессинга Sass). В этой статье сочетаются обе эти тяги. Даже если это не понадобится в очередном проекте для клиента, однозначно стоит экспериментировать и раздвигать границы, чтобы как следует понять сам язык. Вы тоже можете делать игры на чистом CSS!

Counter-Increment

counter-increment – довольно малоизвестное CSS-свойство, изначально предназначенное для нумерования оглавлений. оно весьма практично и при этом отлично поддерживается:

caniuse

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

2 Комментарии

  1. Алексей

    «Значения для counter-reset и counter-reset должны совпадать» видимо «counter-increment»

    1. Максим Усачев (Автор записи)

      Да, вы правы. Спасибо, поправил!

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

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

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

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