Статьи

CSS3 Pong: безумные вещи, которые нужно сделать с CSS # 17

Pop Quiz: Что это?

Прежде чем мы начнем, давайте попробуем быстро «Кто я?». Это картина ..

а). Имперский Звездный Разрушитель из Звездных Войн?

б). Модель Lego роторного двигателя Ванкеля?

с). Научный калькулятор?

Если вы ответили « C — калькулятор », поставьте себе большую улыбку на лице! Вы смотрите на полнофункциональный научный калькулятор, полностью построенный в виртуальном мире Minecraft .

Minecraft — если вы с ним не знакомы — это игровой мир онлайн-строительства с очень 8-битной эстетикой. Думайте «виртуальный лего».

Большинство Minecrafters тратят свое время на строительство тщательно продуманного дворца / крепостей. Иногда эти структуры включают относительно редкие «красные камни», которые можно использовать как простые переключатели для управления дверями, воротами и т.п.

В приведенном выше примере 16-летний подросток («MaxSGB») соединил виртуальную гору красного камня в рабочий калькулятор размером со стадион — в комплекте с графическими способностями, функциями синуса, косинуса, загара и квадратного корня.

Если у вас возникли проблемы с решением этой проблемы, представьте, как ребенок строит свою собственную Playstation II из Lego Mindstorm — вы приближаетесь.

Удивительный. Сумасшедший. Смешной. Стойкий. Я не уверен, что это лучшее описание.

Это хороший калькулятор?

Нет. Реальность такова, что у меня в 5 классе были цифровые часы, у которых был лучший встроенный калькулятор, чем это творение.

Но дело не в этом.

Раздвигая границы, мы можем только догадываться о том, чему этот парень научился на этом пути — уроках во всем, от архитектуры до инженерии, от программирования до электроники, до простого решения проблем.

План

С этой идеей подталкивать вещи, пока они не содрогаются от боли, мы собираемся сделать что-то столь же глупое с CSS — создать играбельную игру — Понг (1972) . Нет JavaScript или Flash. Просто HTML, CSS и здоровая порция психованного профессора.

По пути мы будем:

  1. делать интересные вещи с помощью CSS-анимации
  2. возиться с селекторами братьев и сестер CSS3
  3. надеюсь, мы посмеемся над изобретательностью / безумием, которые мы используем

Мы собираемся использовать много очень современных CSS3. Теоретически, он * должен * работать в Firefox, Opera и Safari, но я только собираюсь гарантировать Chrome для этого демонстрационного кода.

Я также собираюсь процитировать официальную версию CSS для W3C в примерах, чтобы сохранить код в беспорядке, хотя реальным браузерам понадобятся свои префиксы (-moz, -webkit и т. Д.).

Большая часть моего демонстрационного кода использует prefixfree для добавления соответствующих специфичных для браузера префиксов во время выполнения, но вы можете предпочесть Compass , Fire , Scout , Sass , Less или даже жестко кодировать CSS своей руки с префиксом.

Если вы хотите знать, к чему мы стремимся, вот последняя демонстрация .

Часть 1 — Анимация

1). Создание суда

Во-первых, давайте настроим корт и мяч для игры.

Вот наша начальная разметка:

<h1>CSS PONG</h1> <div id="court"> <div id="horizontal"> <span id="ball"></span> </div> </div> 

Внешний #court DIV определяет нашу игровую площадку. Мы дадим ему пунктирную зеленую границу, как в оригинальной игре.

У нас есть #ball, который находится внутри упаковочного DIV, называемого #horizontal, который работает на всю ширину корта. Этот невидимый DIV станет полезной платформой для повешения других элементов экрана.

 #court{ margin: 30px auto; width: 600px; height: 300px; position: relative; border: 4px dotted #3f3; 

Нашему # корту нужна сеть. Мы можем использовать псевдоэлемент «: before», чтобы прикрепить его к нашему двору.

 #court:before { left: 50%; content:''; width: 10px; height: 300px; position: absolute; border-left: 4px dashed #3f3; } 

Наш мяч — простой DIV с круглыми углами 20 на 20 пикселей. Внешнее изображение не требуется.

 #ball { position: absolute; width: 20px; height: 20px; display: block; background :#3f3; border-radius: 50%; transform: translate3d(10px,0,0) } 

Теперь у нас есть стартовая структура.

2). Анимация мяча

Давайте вернемся немного назад. Если вы уже являетесь мастером CSS анимации, смело переходите на страницу Back to Pong . Если нет, мы разберем несколько общих «нужно знать».

Во-первых, большинство, но НЕ ВСЕ, свойства CSS являются «анимируемыми». Как правило, если вы можете описать это числом, оно должно быть анимируемым.

Например, мы можем анимировать:

  • left: 0px → left: 100px
  • margin-top: 10% → margin-top:90%
  • opacity: 0 → opacity: 1
  • z-index: 1 → z-index: 99

Тем не менее, мы не можем оживить:

  • float: left → float: right
  • display: block → display: none
  • visibility: hidden → visibility: visible

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

К счастью, цвета всегда легко анимировать — даже если вы используете экзотические ключевые слова, такие как «темная орхидея» или «васильковый» — потому что ваш браузер автоматически конвертирует их в числа.

Но это еще не все.

Часто мы хотим перемещать вещи по экрану, и есть много способов сделать это. Теоретически, анимация любого из этих свойств будет работать:

  • left
  • right
  • top
  • bottom
  • margin
  • padding
  • transform:translate( x,y )
  • transform:translate3d( x,y,z )

Хотя все они работают, как ни удивительно, есть большие различия в том, как браузеры обрабатывают их.

Хотите верьте, хотите нет, translate3d() — самый эффективный способ перемещения объектов с помощью CSS3 — даже если вы только анимируете 2d объекты!

По-видимому, translate3d() разгружает работу анимации на графический процессор, что значительно ускоряет процесс. Конечно, мой процессор MacBook Pro оставался намного круче, когда я использовал translate3d() .

Вывод: попробуйте использовать translate3d() всякий раз, когда вы перемещаете объекты по экрану.

Вернуться к понг.

Давайте возьмем то, что мы знаем о translate3d() , и дадим нашему мячу простой отскок слева направо и назад.

transform:translate3D запрашивает у нас три значения — X, Y и Z. Анимация X перемещает объекты влево и вправо, в то время как анимация Y перемещает вещи вверх и вниз. Мы можем оставить значение Z на 0.

Давайте создадим анимацию «влево» и применим ее к нашему #ball.

 @keyframes leftright { 0%,100% {transform: translate3d(10px,0,0)} 50% {transform: translate3d(570px,0,0)} } #ball{ ... animation: leftright 2s infinite linear } 

Мяч теперь должен подпрыгивать слева направо в нашем #court DIV каждые две секунды.

Шаг 1: настройка суда

Теперь давайте создадим новую анимацию под названием «Updown» — вы можете догадаться, что это делает — и примените ее к нашей горизонтальной платформе.

 @keyframes updown { 0%,50%,100% {transform: translate3d(0,0,0)} 25% {transform: translate3d(0,142px,0)} 75% {transform: translate3d(0,-136px,0)} } #horizontal{ ... animation: updown 2s infinite linear; } 

Вот что мы имеем до сих пор .

Как вы можете видеть, наш мяч совершает идеальную ромбовидную петлю корта каждые две секунды.

Приятно … но быстро становится скучно.

Что произойдет, если мы сделаем небольшую настройку времени анимации? Вместо 2,0 секунд давайте выставим #ball на цикл каждые 1,9 секунды, а анимацию платформы # horizontally — на 2,3 секунды.

Изменение времени в наших анимациях делает путь намного интереснее

Вот результат .

Как вы можете видеть, это небольшое изменение времени оказывает большое влияние на путь мяча. Это правда, этот путь будет повторяться, но вместо двухсекундных циклов повторения, теперь он на 46 секундном цикле.

Гораздо более интересный результат.

Добавление игроков

Давайте добавим несколько игроков в микс. Мы вставим два новых SPAN в нашу платформу (# player1 и # player2) и разместим их с обоих концов.

 <h1>CSS PONG</h1><div id="court"> <div id="platform"> <span id="ball"></span> <span id="player1"></span> <span id="player2"></span> </div> </div> 
 #player1, #player2 { background:#3f3; position:absolute; width:7px; height:44px; left:4px; margin-top:-12px; } #player2{right:4px} направо #player1, #player2 { background:#3f3; position:absolute; width:7px; height:44px; left:4px; margin-top:-12px; } #player2{right:4px} 

Вот рабочая демка .

Поскольку игроки находятся внутри нашей движущейся платформы, они действительно НЕ МОГУТ ПОМОЧЬ, но отлично отслеживают мяч сверху вниз.

Но мы знаем, что настоящие игроки так не играют. Они дергаются. Они бросаются. Они ныряют. Они карабкаются.

Что если мы создадим новый «дергающийся» ключевой кадр анимации, который заставит летучих мышей дергаться беспорядочно — по отношению к платформе?

 @keyframes twitchy { /* make player twitch like a real player */ 0%, 100%{transform: translate3d(0, 0px, 0); } 20% {transform: translate3d(0, -45px, 0); } 44% {transform: translate3d(0, 25px, 0); } 46% {transform: translate3d(0, -15px, 0); } 48% {transform: translate3d(0, 15px, 0); } 50% {transform: translate3d(0, 50px, 0); } 70% {transform: translate3d(0, 60px, 0); } 85% {transform: translate3d(0, -30px, 0); } 95% {transform: translate3d(0, 30px, 0); } } #player1, player#2{ animation: leftright 2.3s infinite linear; } #player1 {animation-delay:1.15s} /* delay player 2 for half a cycle */ 

Теперь, пока мы сохраняем анимацию летучей мыши в то же время, что и анимация мяча «влево», они всегда должны встречаться с шаром между подергиваниями.

Давайте поместим это в демо-версию (в ESPN super slo-mo!).

Это улучшение. Летучие мыши дергаются взад-вперёд, как пара 7-летних на слишком большом красном сердечном фоне.

Итак, на данный момент у нас есть интересный пример анимации, но это не игра.

Часть 2: сделать его играбельным… вроде

« Эй, разве это не было бы круто, если бы ты мог играть на самом деле?

Это был первый комментарий Кейси, когда он увидел анимационный эксперимент. Я смеялся и пожал плечами в то время, но это заставило меня задуматься о следующих нескольких днях.

Очевидно, что разница между игрой и анимацией заключается в интерактивности . Если я не использую JavaScript, мне нужно, чтобы мой CSS взаимодействовал с пользователем. Я перечислил все встроенные «пользовательские события», которые CSS может использовать.

  • :hover
  • :active
  • :visited
  • :checked
  • :focus
  • :enabled
  • :disabled
  • :selection
  • :target

Может быть, мы могли бы каким-то образом совмещать одно из этих событий? Я решил :hover было моим лучшим шансом, поэтому наш курсор станет нашей летучей мышью.

 #court { cursor: url(/bat.gif), text; } 

Далее нам нужен новый элемент, который будет действовать как наш триггер для анимации. Я собираюсь вставить DIV под названием «#targetzone» как раз перед нашей платформой #horizontal. Мы также можем потерять автоматизированного игрока №1.

 <h1>CSS PONG</h1> <div id="court"> <div id="court"> <span id="targetzone"></span> <div id="horizontal"> <span id="ball"></span> <span id="player2"></span> </div> </div> 
 #targetzone { background: rgba(0,0,255,0.25); position: absolute; margin:-50% 0 0 -50%; z-index:100; width:800px; height:600px; } 

CSS-селекторы — « + и « ~ — довольно мощные, но часто упускаются из виду. В этом случае мы можем использовать их для передачи состояния :hover из одного элемента HTML в элемент рядом с ним. Это позволяет нам использовать #targetzone для запуска нашей анимации.

Итак, нам нужно удалить все наши текущие анимации и перенести их в новые правила «запуска при наведении», например:

 #targetzone:hover{ animation: updown 2.3s infinite linear; } #targetzone:hover + #horizontal{ animation: updown 2.3s infinite linear; } #targetzone:hover + #horizontal #player2{ animation: twitchy 1.9s infinite linear; animation-delay:0.95s; } #targetzone:hover + #horizontal #ball{ animation: leftright 1.9s infinite linear; } 

Теперь наша анимация воспроизводится только тогда, когда курсор находится над этой #targetzone — я сделал полупрозрачный синий #targetzone для демонстрации, но обычно он невидим для игрока.

Как это Мы приближаемся.

Теперь давайте подумаем о ЛЮБОЙ реальной игре в теннис.

Самое смешное в теннисе: в большинстве случаев игра НЕ НУЖНА.

Вы не можете поймать, вести мяч или нести мяч. Для 98% каждой точки мяч перемещается (или оживляется) через корт, вне вашей досягаемости или прямого влияния.

В течение буквально микросекунд каждого ралли у вас есть шанс либо отбить мяч назад, либо ошибиться.

Итак, если разбить его на шаги, наша теннисная логика может быть записана так:

1) НАЧАЛО: анимация мяча.
2) АНИМАТ: мяч противнику. Игнорирование игрока
3) АНИМАТ: мяч обратно игроку. Игнорирование игрока
4) CHECK: положение курсора игрока
5) Игрок блокирует путь мяча?
5) ЕСЛИ: ДА — ПОЙТИ: Шаг № 2
6) ЕСЛИ: НЕТ — КОНЕЦ анимации и СЧЕТ 1 для компьютера.

Поэтому, если мяч приблизится к базовой линии, мы должны будем быстро сместить наш триггер #targetzone в область перед мячом, игрок должен будет находиться в этом месте, чтобы анимация продолжалась. У нас было бы какое-то очень, очень элементарное CSS ‘обнаружение столкновений’.

Вот новая анимация ключевого кадра для изменения размера #targezone.

 @keyframes targetzone { /* ball is in general play - targetzone is big */ 0%, 96% { width: 800px; height: 600px; margin: -50% 0 0 -50%; } /* ball is approaching player 1's baseline - targetzone shrinks*/ 96.1%, 100% { width: 150px; height: 100px; top: 0; left: 0; margin: 10% 0 0 -50px; } } 

Используя запятую, мы можем добавить эту новую анимацию в наш триггер #targetzone.

 #targetzone:hover { animation: updown 2.3s infinite linear, targetzone 1.9s infinite linear; } 

Вот результат.

По сути, это работает, как и планировалось, но с одной проблемой.

В микросекунду любой игрок пропускает мяч (и состояние зависания заканчивается), #targetzone сбрасывает и мгновенно повторно запускает анимацию, чтобы начать снова. Вы должны обратить пристальное внимание, чтобы даже заметить, что точка закончилась.

Нам нужна еще одна анимация, чтобы сделать короткий перерыв между точками (как настоящий теннис). Эта анимация будет воспроизводиться только один раз (а не зацикливаться) для каждого нового ралли и будет изменять форму #targetzone с height: 0 до height: 150px 150 пикселей.

 @keyframes preparetoserve { /* Give targetzone no height so trigger is hidden */ 0%, 90% { height: 0px; } /* after a short time, resize the targetzone, ready to serve*/ 90.1%, 100% { height: 100px; } } 

Мы можем прикрепить его непосредственно к #targetzone, потому что нам не нужен проигрыватель для его запуска.

 #targetzone { ... animation: preparetoserve 2s 1 linear; } 


Давайте добавим это в демо.

Это принимает форму!

Часть 3: ведение счета

Как сказал Оскар Уайльд: «Ничто так не преуспевает, как излишки», так зачем останавливаться сейчас? Этому щенку нужна система подсчета очков на основе CSS, гошдарнит!

Теперь, вместо того, чтобы разбивать каждую строку кода — это уже длинная статья — я собираюсь рассказать вам об общем принципе и оставлю вас, чтобы вы могли разобрать демо, если вам интересно.

Поддержание состояния с помощью HTML и CSS

Чтобы обновить любой счет, нам нужен способ использовать HTML / CSS для «поддержания состояния» — чтобы отслеживать, что произошло до сих пор. Обычно это обрабатываются серверами и файлами cookie.

Пару лет назад Леа Веру написала действительно забавную статью об использовании рестайлинговых флажков для создания игры в крестики-нолики на основе CSS . Он показал, как HTML-формы могут записывать щелчки мыши и как CSS может мгновенно реагировать на эти клики. Она также показала, что элементы формы HTML не должны выглядеть как элементы формы HTML.

Я думал, что это супер умные вещи.

Итак, что если

1). Я заменил свой #Targetzone SPAN серией из пяти HTML-кнопок — по одной на каждую набранную точку (1-5)? Немного переназначив CSS, я мог бы сделать так, чтобы наша анимация требовала, чтобы переключатель был одновременно отмечен и наведен (то есть input:checked:hover ).

2). Я также связал отображение табло с каким из этих входов проверяется? Табло будет тикать, как только Игрок 1 нажмет, чтобы подать. Да, печальная правда в том, что компьютер в настоящее время не может проиграть эту игру — поэтому единственный реальный вопрос — «Сколько времени пройдет, пока Игрок 1 (вы) не пропустит мяч? ».

Я знаю, это кажется несправедливым, когда вы узнаете. Это.

Конечно, ради театра мы хотим, чтобы наш игрок думал, что у него есть шанс на победу. Дело в том, что мы можем обновить табло радиокнопок, только когда происходит щелчок, и нет щелчков, когда кто-то пропускает мяч.

Это означает, что я должен обновить счет на клике службы, но каким-то образом не позволить игроку ВИДЕТЬ этот новый счет, пока не закончится точка. Хммм …

3). Итак, что, если я выставлю новый счет просто вне поля зрения, пока наведен курсор ? Подобно стуку, открывающему старую оконную панель карандашом, окно рухнет, как только мы вытащим карандаш. В нашем случае удаление парения приведет к падению нашего нового счета.

CSS-переход обеспечивает плавное движение нового счета.

Я не говорил, что это красиво.

Но это работает.

Вот версия окончательного кода Codepen:

Не стесняйтесь ковыряться там.

Проблемы, предостережения и проблемы

1). Возможно, вы уже случайно обнаружили это. Если вы не передвинете курсор, мяч будет возвращен — ДАЖЕ, ЕСЛИ ВАС НИКОГДА НЕ БУДЕТ ЕГО.

К сожалению, это всего лишь фундаментальное свойство работы браузеров, и я сомневаюсь, что существует какой-либо способ его кодирования (в любом случае, без использования JavaScript).

По сути, каждый раз, когда вы перемещаете курсор — даже малейшее количество — ваш браузер вызывает вопрос «Эй, есть ли какие-нибудь новые события при наведении?» Если ответ «да», он отображает новый CSS.

Однако, если мышь не двигается, браузер просто не задает этот вопрос, даже если другие элементы переместились относительно курсора. Я не могу думать, как вы могли бы изменить это поведение (без JavaScript).

Эй, перестань жульничать, и просто продолжай двигаться, хорошо ?!

2). Я хотел бы иметь возможность ограничить движение летучей мыши игрока # 1 строго вверх и вниз. Если вы можете придумать, как это осуществить, я дарую вам великую славу, сэр или мадам!

3). У меня есть смутное представление, что иногда робот-игрок может проиграть. Если мы установим лимит на анимацию — скажем, 20-25 итераций — и человек продержится так долго, мы сможем позволить им победить.

Тем не менее, забить его правильно это сложная часть. Как мы можем сказать, что кто-то пережил бота, и скорректировать счет соответственно? Я немного подумал об этом, но это вредит моему мозгу.

Если вы склонны, не стесняйтесь раскошелиться на CodePen и попробуйте .

Последнее слово

Итак, мы на пороге новой блестящей эры игр CSS3?

Хм … нет.

Чтобы уточнить. Нет.

Это был не просто взлом. Это был сверкающий город хаков, построенный на берегу реки Олд-Хак. На Планете Хак.

Итак, зачем?

Самое интересное для меня — это попросить CSS сделать что-то, для чего он не предназначен. Было довольно сложно сделать что-то относительно простое с другими языками. Это была просто забавная проблема, чтобы думать во время игры. Как судоку или кубик Рубика.

Во-вторых, как и парень из Minecraft, есть некоторые вещи, которые вы можете по-настоящему выучить, если вы выйдете за пределы того, что вы обычно делаете. Мне пришлось заставить себя по-настоящему взглянуть на спецификацию CSS свежим взглядом впервые за долгое время.

Если повезет, эта статья даст некоторым людям идеи об использовании CSS способами, которые иначе не могли бы прийти им в голову.

Кроме того, если вы сделали это далеко, я благодарю вас за ваше терпение! Я старался держать это коротко, но было много чего объяснить.

* Так что ты думаешь?
* Есть улучшение?
* Есть вопрос?
* Есть идея для CSS3 на World of Warcraft?