
Доброго времени суток, друзья!
В этой статье, как следует из названия, мы займемся реализацией одного занимательного эффекта с использованием одной любопытной библиотеки (anime.js). Я не буду петь дифирамбы этой библиотеке, но тем, кто плотно занимается анимацией, определенно стоит обратить на нее внимание. Простой интерфейс + отличная документация, что еще нужно для творчества?
По материалам этой замечательной статьи.
Автор указанной статьи назвал свой эффект «Анимация следа из частиц» (Particle Trail Animation).

Мы возьмем код из статьи, подробно разберем его и немного улучшим.
Во-первых, сделаем частицы разноцветными. Потому что один цвет — это скучно.
Во-вторых, анимация будет запускаться по клику. Потому что бесконечная анимация раздражает.
В-третьих, привяжем центр анимации (спирали) к месту клика. Потому что пользовательский опыт прежде всего.
Мы не будем использовать холст (canvas). Анимироваться будет множество маленьких блоков (div).
Итак, поехали (как сказал Гагарин, отправляясь в космос).
Из чего состоит наш HTML? В нем мы подключаем библиотеку, наш скрипт и, собственно, все.
Стили также сложно назвать впечатляющими:
body {
margin: 0;
background: #333;
overflow: hidden;
}
/* это частицы */
.dot {
position: absolute;
/* они круглые */
border-radius: 50%;
}
Даешь JavaScript! As you wish.
Оборачиваем весь наш код в обработчик события «клик» объекта Window:
window.addEventListener('click', event => { ... })
Создаем контейнер для частиц с классом «box»:
let box = document.createElement('div')
box.classList.add('box')
document.body.appendChild(box)
Определяем некоторые значения (не магические числа, don't worry, be happy):
// количество частиц для одного угла
let n = 10
// количество поворотов угла
// это количество определяет размер спирали
let a = 20
// количество углов
// это количество определяет количество витков спирали
let l = 110
Генерируем частицы с помощью двух циклов for:
for (let i = 0; i <= l; i++) {
// меняем угол
let angle = 0.1 * i
// делаем место клика центром спирали
let x = (a * angle) * Math.cos(angle) + event.clientX
let y = (a * angle) * Math.sin(angle) + event.clientY
// создаем n частиц для каждого угла
for (let j = 0; j < n; j++) {
// создаем частицу с классом "dot"
let dot = document.createElement('div')
dot.classList.add('dot')
box.appendChild(dot)
// определяем размер частицы с помощью метода "anime.random()"
// позволяющего получать произвольные значения
// в заданном диапазоне
let size = anime.random(5, 10)
// стилизуем частицу
// добавляем атрибут "style"
dot.setAttribute('style',
// с помощью шаблонного (строкового) литерала определяем
//ширину и высоту
`width: ${size}px; height: ${size}px;
// положение с помощью anime.random()
top: ${y + anime.random(-15, 15)}px; left: ${x + anime.random(-15, 15)}px;
// делаем частицу полностью прозрачной по умолчанию
opacity: 0;
// цвет фона с помощью функции получения произвольного цвета
background: #${ функция получения произвольного цвета }`)
}
}
Функция получения произвольного цвета может выглядеть так:
function getRandomColor() {
let letters = '0123456789abcdef',
color = '#'
for (let i = 0; i < 6; i++) {
color += letters[Math.trunc(Math.random() * 16)]
}
return color
}
Но мы не ищем легких путей, поэтому она будет выглядеть так:
(Math.random()*0xffffff<<0).toString(16)
Здесь используется побитовый сдвиг влево и приведение к шестнадцатиричной строке.
Далее созданные нами частицы анимируются с помощью «anime»:
anime({
// цель анимации
targets: document.querySelectorAll('.dot'),
// повторение
loop: false,
// временная функция анимации
// в нашем случае линейная (равномерная)
easing: 'linear',
// объекты анимации
// прозрачность
// эффект проявления и затухания
opacity: [
{
value: 1,
// продолжительность
duration: 50,
// задержка
delay: anime.stagger(2)
},
{
value: 0,
duration: 1200
}
],
// эффект расширения (роста)
// ширина
width: {
value: 2,
duration: 500,
delay: anime.stagger(2)
},
// высота
height: {
value: 2,
duration: 500,
delay: anime.stagger(2)
},
// дополнительный разброс
// смещение по оси х
translateX: {
value: () => anime.random(-30, 30),
duration: 1500,
delay: anime.stagger(2)
},
// смещение по оси y
translateY: {
value: () => anime.random(-30, 30),
duration: 1500,
delay: anime.stagger(2)
}
})
Last, but not least, добавляем в начало проверку наличия box:
if (document.querySelector(".box")) {
document.body.removeChild(document.querySelector(".box"));
}
Результат:
Код на GitHub.
That's all, folks! Благодарю за внимание.