Создаем крутую анимацию «след частиц» на JavaScript
Вы когда-нибудь задумывались о том, чтобы увлекать посетителей вашего веб-сайта на несколько секунд причудливой, блестящей анимацией, в то время как некоторые данные загружаются в фоновом режиме? К счастью, нет необходимости углубляться в графическое программирование с использованием 3D-библиотек, таких как three.js. Все, что вам нужно, это базовые знания CSS и JavaScript и легкая библиотека анимации, такая как anime.js. В итоге мы должны получить следующий результат:
Скачиваем и внедряем Anime.Js
Вы можете скачать библиотеку anime.js с официального сайта GitHub. Загрузите файл anime.js или anime.min.js для использования в своем проекте.
В этом примере файл HTML выглядит следующим образом:
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Anime.js Particles</title>
<!--or use anime.min.js-->
<script src="anime.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="anime-container">
</div>
<script src="script.js"></script>
</body>
</html>
Файл CSS styles.css определяет цвет фона для страницы и для отдельных частиц. Настройки позиционирования необходимы, чтобы мы могли позже свободно размещать частицы на странице, используя свойства CSS left
и top
.
body {
background-color: hsl(30, 3%, 14%);
}
.anime-container {
position: relative;
}
.anime-container .dot{
position: absolute;
/*draw particles as circles:*/
border-radius: 50%;
background-color: hsl(60, 100%, 80%);
}
Генерация «частиц»
Как следует из названия, анимация частиц состоит из множества мелких частиц, движущихся в пространстве по определенной схеме. Все частицы генерируются одновременно до начала анимации.
Для следующего объяснения будет полезна официальная документация по anime.js.
В этом примере частицы расположены на архимедовой спирали. x
и y
положение частицы на экране (они же left
и top
в CSS) рассчитывается исходя из позиции angle
по спирали:
x=a*angle*cos(angle)
y=a*angle*sin(angle)
Количество углов и, следовательно, длина спирали определяется параметром l
. С помощью параметра a
вы можете контролировать плотность спирали.
var container = document.querySelector(".anime-container");
var n = 15;
var a = 20;
var l = 110;
for (var i = 0; i <= l; i += 1) {
var angle = 0.1 * i;
//shift the particles to the center of the window
//by adding half of the screen width and screen height
var x = (a*angle) * Math.cos(angle) + window.innerWidth / 2;
var y = (a*angle) * Math.sin(angle) + window.innerHeight / 2;
var dot = document.createElement("div");
dot.classList.add("dot");
container.appendChild(dot);
var size = 5;
dot.style.width = size + "px";
dot.style.height = size + "px";
dot.style.left = x + "px";
dot.style.top = y + "px";
dot.style.backgroundColor = "hsl(60, 100%, 80%)";
}
}
Таким образом, мы получаем спираль с одной частицей на позицию, но реальный эффект следа может быть достигнут только в том случае, если в каждой позиции генерируется более одной частицы. Чтобы спираль выглядела широкой, положение отдельных частиц должно немного отличаться. Библиотека anime.js предоставляет практическую вспомогательную функцию для этого:
anime.random(minValue, maxValue);
Размер частиц также изменяется случайным образом:
for (var i = 0; i <= l; i += 1) {
var angle = 0.1 * i;
//shift particles to the center of the window
//by adding half of the screen width and screen height
var x = (a*angle) * Math.cos(angle) + window.innerWidth / 2;
var y = (a*angle) * Math.sin(angle) + window.innerHeight / 2;
var n = 15;
//create n particles for each angle
for (var j = 0; j < n; j++) {
var dot = document.createElement("div");
dot.classList.add("dot");
container.appendChild(dot);
var size = anime.random(5, 10);
dot.style.width = size + "px";
dot.style.height = size + "px";
dot.style.left = x + anime.random(-15, 15) + "px";
dot.style.top = y + anime.random(-15, 15) + "px";
dot.style.backgroundColor = "hsl(60, 100%, 80%)";
}
}
Перед началом анимации все частицы должны быть невидимыми. Поэтому добавим:
dot.style.opacity = "0";
Основные настройки анимации
Основные настройки анимации сделаны следующим образом:
- Анимация должна повторяться непрерывно (loop: true),
- Ослабление является линейным (но можно попробовать различные значения),
- Целевыми элементами являются элементы с классом .dot
anime({
loop: true,
easing: "linear",
targets: document.querySelectorAll(".dot"),
});
На следующем шаге будем анимировать различные свойства CSS. Основные шаги для CSS-анимации можно найти в документации anime.js.
Анимация непрозрачности
Вот как выглядит наша первая анимация, в которой все частицы медленно становятся видимыми в течение 50 мс:
anime({
loop: true,
easing: "linear",
targets: document.querySelectorAll(".dot"),
opacity: { value: 1, duration: 50}
});
И теперь мы наконец раскроем трюк, который создает спиральное движение частиц! Идея состоит в том, чтобы сделать частицы видимыми с определенной временной задержкой (например, с интервалом в 2 мс). Частицы в середине спирали сначала становятся видимыми, а затем все остальные частицы изнутри наружу. Функция пошагового изменения в anime.js идеально подходит для этого.
opacity: { value: 1, duration: 50, delay: anime.stagger(2) }
Чтобы создать иллюзию следа, частицы должны начать медленно исчезать, как только они появились. К счастью, anime.js предоставляет инструменты для этого:
opacity: [
{ value: 1, duration: 50, delay: anime.stagger(2) },
{ value: 0, duration: 1200}
],
Анимация размера
След кометы должен казаться больше на переднем плане, чем на заднем. Для этого частицы сжимаются в течение 500 мс до диаметра 2 пикселя. Важно выбрать ту же задержку, что и для анимации непрозрачности, чтобы каждая частица начинала уменьшаться только после того, как она появилась:
width: { value: 2, duration: 500, delay: anime.stagger(2) },
height: { value: 2, duration: 500, delay: anime.stagger(2) },
Индивидуальное движение частиц
Типичная вещь в анимации частиц — это индивидуальное, непредсказуемое поведение частиц. Теперь наконец оживим частицы с индивидуальным движением в x
и y
направлении:
translateX: {
value: function() {
return anime.random(-30, 30);
},
duration: 1500,
delay: anime.stagger(2)
},
translateY: {
value: function() {
return anime.random(-30, 30);
},
duration: 1500,
delay: anime.stagger(2)
}
Опять же, важно, чтобы движение начиналось с той же временной задержкой, что и появление частиц.
Кроме того, в этом случае необходимо рассчитать значения для translateX
и translateY
.
Вы можете увидеть окончательный результат:
Вы можете изменить анимацию на свой вкус, просто настроив все значения. У нас есть небольшой совет для последних штрихов: теперь, когда мы знакомы с параметрами на основе функций, анимацию непрозрачности можно немного улучшить:
opacity: [
{ value: 1, duration: 50, delay: anime.stagger(2) },
{ value: 0, duration: function(){return anime.random(500,1500);}}
],
Время до исчезновения частицы теперь устанавливается для каждой частицы отдельно. Это делает нашу анимацию визуально еще более сложной.
Теперь вы познакомились с прекрасной библиотекой anime.js
Конечный вариант файла script.js:
var container = document.querySelector(".anime-container");
var a = 20;
var l = 110;
for (var i = 0; i <= l; i += 1) {
var angle = 0.1 * i;
var x = (a*angle) * Math.cos(angle) + window.innerWidth / 2;
var y = (a*angle) * Math.sin(angle) + window.innerHeight / 2;
var n = 15;
for (var j = 0; j < n; j++) {
var dot = document.createElement("div");
dot.classList.add("dot");
container.appendChild(dot);
var size = anime.random(5, 10);
dot.style.width = size + "px";
dot.style.height = size + "px";
dot.style.left = x + anime.random(-15, 15) + "px";
dot.style.top = y + anime.random(-15, 15) + "px";
dot.style.opacity = "0";
}
}
anime({
loop: true,
easing: "linear",
opacity: [
{ value: 1, duration: 50, delay: anime.stagger(2) },
{ value: 0, duration: function(){return anime.random(500,1500);}}
],
width: { value: 2, duration: 500, delay: anime.stagger(2) },
height: { value: 2, duration: 500, delay: anime.stagger(2) },
targets: document.querySelectorAll(".dot"),
translateX: {
value: function() {
return anime.random(-30, 30);
},
duration: 1500,
delay: anime.stagger(2)
},
translateY: {
value: function() {
return anime.random(-30, 30);
},
duration: 1500,
delay: anime.stagger(2)
}
});