Игра Яндекс Практикума
Игра Яндекс Практикума
Игра Яндекс Практикума

Знакомство с впечатляющими возможностями SVG-анимаций: пишем небольшую игру

Отредактировано

15К открытий15К показов
Знакомство с впечатляющими возможностями SVG-анимаций: пишем небольшую игру

Рассказывает Грэг Хованесян 

Идея игры

Я уже достаточно давно знаком с SVG-анимациями и хорошо понимаю, как можно их использовать для создания анимированных спрайтов или дизайна веб-страниц. Такое сочетание, как GreenSock и нативный CSS, работало прекрасно. Поэтому я подумал и решил пойти дальше — создать с их помощью небольшую игру. Разумеется, с отлично проработанным, анимированным дизайном. Итак, однажды ночью ко мне пришла идея игры, я сделал пару набросков и показал их брату — профессиональному дизайнеру. Мы обсудили все детали, и я приступил к работе. 

Как играть: Прыгающий мяч меняет цвет. мы должны следить за его цветом и изменять цвет колонн, на которые он падает, чтобы цвета совпадали. Одно нажатие на колонну делает её красной, два — жёлтой, три — фиолетовой.

Процесс создания

Во время работы над игрой я сталкивался с различными проблемами. Самым сложным было сделать так, чтобы игра выглядела одинаково хорошо на всех экранах. После нескольких попыток стало ясно, что классические техники использования медиазапросов не сработают.

Это не руководство, поэтому я не буду подробно объяснять каждую строчку кода. Но я покажу вам несколько классных приёмов, а вы сможете поиграться с кодом на CodePen. Кроме того, я поделюсь с вами нескольким полезными материалами. Код также сопровождается комментариями.

GSAP — это очень мощный инструмент, поэтому неудивительно, что я им пользовался. Кроме того, у CodePen есть встроенный компилятор Babel, поэтому я могу писать там ES6-код, у которого есть куча возможностей: синтаксис классов, стрелочные функции и т.д. Подробнее об этом можно узнать здесь.

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

Фоновая анимация

Всё, что вы видите на фоне — это SVG. Каждая волна, каждый слой гор и облака — это отдельные div’ы. Да можно было бы создать фон лишь одним SVG, и анимировать дочерние элементы — Greensock это может, достаточно задать им ID. Дело в том, что тогда анимация не будет работать на мобильных устройствах.

			var wave1 = TweenMax.to('.wave1', 0.7, {backgroundPositionX: '-=54px', repeat: -1, ease: Power0.easeNone});
		

Здесь мы используем инструмент TweenMax для сдвига фона на 54 пикселя с постоянной скоростью. То же самое мы делаем и с горами и облаками. Задавая каждому элементу уникальную скорость, мы получаем неплохо выглядящий эффект параллакса.

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

			{rotation: 360, transformOrigin: "-"+radius+"px -"+radius+"px", ...}
		

Они все медленно вращаются, и это выглядит немного хаотично.

Анимация колонн

В каждой колонне есть маленькие движущиеся элементы. Их я создал просто средствами HTML/CSS. SCSS сэкономил огромное количество времени и строк кода. Я создал отдельные миксины для каждой фигуры внутри колонны. Взглянем на эффект для пузырьков. Каждый круг имеет абсолютное позиционирование, а его стиль задаётся миксином. Создание треугольников в CSS занимает ещё больше строк кода, поэтому миксины выглядят ещё выгоднее.

Посмотрите на красную колонну с пузырьками.

			@mixin bubble ($size, $top, $left) {
    height: $size;
    width: $size;
    top: $top;
    left: $left;
}

@mixin bubble_hollow ($size, $top, $left) {
    @include bubble ($size, $top, $left);
    background-color: transparent;
    border-style: solid;
}
		

Таким образом, если я хочу добавить новый пузырёк, я просто вызываю нужный миксин, указывая расположение пузырька в колонне.

			.bubble-4 {
    @include bubble(15px, 98px, 37px);
}
.bubble-5 {
    @include bubble_hollow(5px, 116px, 20px);
}
		

Анимация пузырьков

Для такого эффекта я использовал отложенные анимации. Они затрагивают все элементы внутри колонны, но двигаться они начинают с неольшими задержками. Больше об этом приёме можно прочитать здесь.

Анимация треугольников

В жёлтой колонне я добавил эффект переворота. Заметьте, что некоторые из элементов вращаются вокруг вертикальной оси, а некоторые — вокруг горизональной. Здесь я использую свойство cycle:

			var triangle = new TimelineMax({delay: 0.5});
triangle
        .staggerTo(el.find('.triangle'), 1.5, {
            cycle:{
                rotationY: [0, 360],
                rotationX: [360, 0],
            },
            repeat: -1,
            repeatDelay: 0.1
        }, 0.1);
		

Анимация блоков

То же самое я использую и в третьей колонне: часть элементов движется налево, часть — направо.

Анимация окна с результатами

Нажмите Rerun, чтобы воспроизвести анимацию.

Я хотел использовать для этого окна эффект желе, и он занял лишь несколько строк:

			var resultTimeline = new TimelineMax();
resultTimeline
      .fromTo('.stop-game .score-container', 0.7, { opacity: 0, scale: 0.3 }, { opacity: 1, scale: 1, ease: Elastic.easeOut.config(1.25, 0.5)})
      .fromTo('.stop-game .final-score', 2, { scale: 0.5 }, { scale: 1, ease: Elastic.easeOut.config(2, 0.5)}, 0)
      .fromTo('.stop-game .result', 1, { scale: 0.5 }, { scale: 1, ease: Elastic.easeOut.config(1.5, 0.5)}, 0.3)
      ;
		

Для создания такого эластичного эффекта нужно всего лишь задать подходящую плавность. Чтобы выбрать подходящую плавность, вы можете воспользоваться этим инструментом: выберите Elastic и определитесь с параметрами.

Flexbox

Конечно же, я использовал Flexbox. (Кстати, совсем недавно его по умолчанию стал поддерживать Bootstrap.) Для тех, кто не знаком с ним, есть отличная вводная статья. Как только вы начнёте использовать Flexbox, вы не сможете жить без него. Итак, взглянем на экран главного меню и самой игры.

Знакомство с впечатляющими возможностями SVG-анимаций: пишем небольшую игру 1

Контейнер “Start Game” задаётся следующим кодом:

			{
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
}
		

flex-direction: column задаёт направление, в котором элементы располагаются в контейнере. Значение по умолчанию (row) разместил бы их в ряд слева направо. Затем идёт justify-content. Он распределяет свободное место, и мы можем выбрать, хотим ли мы добавить промежутки слева, справа или вообще вокруг всего элемента. Мы устанавливаем значение space-between, что поднимает “Top” наверх, “How to Play” размещает внизу, а “Logo Holder” — в центре (всё свободное пространство расположено между этими элементами). align-items: center центрирует элементы относительно горизонтальной оси.

“How to Play” — это гибкий элемент:

			{
    display: flex;
    width: 100%;
}
		

Мы не задаём направление, поэтому три секции размещаются в один ряд. У секций 1 и 3 параметр “flex” установлен в 1, поэтому они занимают всё свободное место. Конечно, можно было использовать justify-content: space-around в элементе “How to Play”, но я хотел, чтобы секция 2 располагалась строго по центру.

Знакомство с впечатляющими возможностями SVG-анимаций: пишем небольшую игру 2

Это сцена игры, со свойством display: flex. В ней есть два контейнера. Я не хотел делать расположение мяча абсолютным, я хотел, чтобы он был ровно над колоннами, и я стремился придумать решение на CSS, чтобы оно работало, даже если я поменяю высоту колонн. Поэтому свойство flex-direction контейнера равно column, и это именно то, что нужно. justify-content: space-between поднимает контейнер с мячом наверх, а контейнер с колоннами закрепляет внизу. Теперь применим к верхнему контейнеру следующие свойства:

			{
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
}
		

Он тоже становится гибким, мы делаем вертикальную ось главной и затем двигаем содержимое к концу контейнера при помощи justify-content: flex-end, поэтому мяч опускается вниз.

Представьте себе реализацию всего этого без Flexbox ?

Делаем игру масштабируемой

Вот что было по-настоящему сложным. Посмотрите, как хорошо игра смотрится на экранах с разным разрешением. Для его я просто использовал CSS-трансформации. Положим разрешение по умолчанию равным 1200x800px. Затем, если ваш экран отличается по разрешению, контейнер игры нужно отмасштабировать при помощи коэффициента screenHeight / 800. Конечно, нужно учитывать и портретное расположение устройства. Вот нужный код:

			var scale = (screenWidth > screenHeight) ? screenHeight/800 : screenWidth/1200;
		

Тем не менее, выглядит это пока что не очень. Окно выглядит очень маленьким.

Знакомство с впечатляющими возможностями SVG-анимаций: пишем небольшую игру 3

Поэтому контейнер нужно увеличить на такой же коэффициент, чтобы он занимал весь экран. Поэтому, если мы увеличим его в два раза, нам нужно увеличить в два раза его исходный размер, чтобы полученный размер стал стал занимать весь экран. А может, экран слишком велик, и нам нужно уменьшить размер в 1.2 раза. В общем, вот нужный код:

			$('.container')
        .css('transform', 'scale(' + this.scale + ')')
        .css('height', height/this.scale)
        .css('width', width/this.scale)
        .css('transformOrigin', 'left top');
		
Следите за новыми постами
Следите за новыми постами по любимым темам
15К открытий15К показов