Использование Anime.js и Howler.js

На прошлой неделе я пережил ужасающий оригинальный сериал Netflix Призраки дома на холме. Это невероятный ужас, который нужно просто проверить. Да, это заставит вас почувствовать себя неловко. Да, ты, наверное, не очень хорошо спишь. Да, вы начнете видеть вещи в своем доме ... Тем не менее, вы также сможете увидеть невероятный актерский состав (детский и взрослый), разбирающийся в семейном горе и травмах, которые преследуют их годами. В этой серии гораздо больше, чем просто страхи от прыжков. Но тебе лучше, блин, поверить, что они тоже есть. 😩🍿

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

Загляните в Codepen и нажмите ручку, если осмелитесь. Тогда читайте дальше, чтобы узнать, как мне это удалось.

Ресурсы

Первое, что я сделал, - это зашел в Netflix и сделал снимок экрана с лучшим изображением двери и ручки. Я сделал этот снимок экрана в Photoshop и вырезал ручку из прозрачного PNG. Я также заметил, что ручка была примерно на 48% от высоты композиции. Затем я использовал штамп клонирования и инструмент с учетом содержимого, чтобы снять ручку с двери. Я также использовал маску градиента, чтобы скрыть дверь во всех направлениях, чтобы изображение работало более отзывчиво, без каких-либо резких визуальных краев. Это дало нам простой фон с тонкой текстурой.

Я также записал каждый из трех звуков поворота ручки из реальной сцены. Затем эта запись была перенесена в Fission и обрезана на три файла для каждого хода. Я записал длину каждого звука в миллисекундах: 5380, 5750 и 8880. Наконец, я последовательно объединил все три звука поворота в один аудиофайл. Подробнее об этом позже.

HTML и CSS

Макет и стиль для этого опыта невероятно просты, поскольку вся логика эффекта создается с помощью Javascript.

Сначала посмотрим на HTML:

<img id="knob" src="knob.png">

Ага ... Я думаю, ты знаешь, что здесь происходит. 😅 Давайте продолжим и добавим наше фоновое изображение, отцентрируем ручку, а затем изменим размер ручки соответствующим образом с помощью CSS:

html, body{
  height: 100%;
  width: 100%;
}
body{
  align-items: center;
  background: url(door.jpg);
  background-position: center center;
  background-repeat: none;
  background-size: cover;
  display: flex;
  justify-content: center;
}
img{
  cursor: pointer;
  height: 48vh;
}

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

JavaScript

Теперь, когда у нас есть жуткая ручка, размещенная в центре нашей страницы, пора создать духа, который будет ее случайным образом поворачивать. 👻🚪 Для сценария анимации я решил использовать Anime.js, потому что подумал, что для этого пригодятся обратные вызовы. Аудио поддерживается Howler.js, потому что мы можем воспользоваться его функциональностью звуковых спрайтов. Но сначала давайте установим несколько переменных:

let turning = false
let rotate  = 0
let turns   = [5380, 5750, 8800]

Позже мы потребуем, чтобы пользователь щелкнул ручку, чтобы начать взаимодействие, и мы будем использовать turning, чтобы отслеживать это начальное взаимодействие. Это необходимо, потому что мобильные устройства требуют взаимодействия с пользователем перед воспроизведением звука. Переменная rotate будет использоваться для отслеживания того, куда в настоящее время повернута наша ручка. turns - это просто массив длительностей наших поворотных аудиоклипов. Вдобавок мы хотим создать звуковой файл с помощью Howler:

let sound = new Howl({
  src: ['knob.mp3'],
  sprite: {
    turn0: [0, 5380],
    turn1: [5380, 5750],
    turn2: [11130, 8800]
  }
})

Обратите особое внимание на свойство sprite. Поскольку мы объединили все звуки поворота в один аудиофайл, мы можем использовать функцию звуковых спрайтов Howler, чтобы определить, где начинается каждый клип и какова его длина. Это делается путем предоставления массива с двумя числами, начальной точкой и длиной клипа в миллисекундах. Я пошел дальше и просто решил это вручную.

С инициализированными переменными и звуком мы можем построить фактическую функцию поворота. Поскольку мне предстояло иметь дело с множеством случайных чисел, я добавил Underscore, чтобы использовать его отличную утилиту _.random. Во-первых, я хотел случайным образом выбрать длину поворота, которую я буду анимировать. Это будет определять продолжительность анимации и, который должен воспроизводиться сопровождающий аудиоклип. Затем я хотел рандомизировать фактическую величину поворота, но при этом сохранить ее пугающе правдоподобной, как в шоу. Я также произвольно выбрал направление вращения. Наконец, если вы смотрите шоу, между поворотами есть небольшая пауза, которую можно установить с помощью свойства Anime.js delay.

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

function turn() {
  let i = _.random(0,2)
  
  let rotation = _.random(45, 100)
  if (Math.random() < 0.5) {
    rotate -= rotation
  } else {
    rotate += rotation
  }
  let delay = _.random(1000, 3000)
  anime({
    targets: '#knob',
    rotate: rotate,
    duration: turns[i],
    delay: delay,
    elasticity: 0,
    easing: 'linear',
    begin: function() {
      sound.play(`turn${i}`)
    },
    complete: function() {
      turn()
    }
  })
}

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

document.getElementById('knob').onclick = function(){
  if (!turning) {
    turning = true
    turn()
  }
}

Надеюсь, вам понравился этот простой урок. Обязательно посмотрите шоу и желаю благополучного Хэллоуина! 🎃