Эффект падающего снега
Эта анимация стала одной из первых, которую я создал с помощью ActionScript в Flash много лет назад. Ниже представлен вариант аналогичного эффекта на HTML, CSS и JavaScript.
Посмотреть демо
В этом руководстве вы узнает, как быстро добавить подобный эффект на свой сайт. А также, как приведенная ниже разметка и код заставляют этот эффект работать.
- Добавляем эффект падающего снега
- Как работает анимация: обзор кода
- Как работает анимация: детальное изучение
- Отправная точка
- Генерируем снежинки
- Цикл анимации
- Реализация Snowflake
- Устанавливаем позицию и размер снежинки
- Специальные возможности и анимации
- Устанавливаем анимацию падающего снега в любой контейнер
- Заключение
Добавляем эффект падающего снега
В нижней части страницы (выше закрывающего тега </ body>) добавьте следующий код:
<style>
#snowflakeContainer {
position: absolute;
left: 0px;
top: 0px;
display: none;
}
.snowflake {
position: fixed;
background-color: #CCC;
user-select: none;
z-index: 1000;
pointer-events: none;
border-radius: 50%;
width: 10px;
height: 10px;
}
</style>
<div id="snowflakeContainer">
<span class="snowflake"></span>
</div>
<script>
// Массив для хранения объектов Snowflake
var snowflakes = [];
// Глобальные переменные для размера окна браузера
var browserWidth;
var browserHeight;
// Указываем количество снежинок, которые должны быть видимыми
var numberOfSnowflakes = 50;
// Флаг для сброса положения снежинок
var resetPosition = false;
// Устанавливаем параметр enableAnimations
var enableAnimations = false;
var reduceMotionQuery = matchMedia("(prefers-reduced-motion)");
// Изменяем значение enableAnimations
function setAccessibilityState() {
if (reduceMotionQuery.matches) {
enableAnimations = false;
} else {
enableAnimations = true;
}
}
setAccessibilityState();
reduceMotionQuery.addListener(setAccessibilityState);
//
// Все начинается здесь...
//
function setup() {
if (enableAnimations) {
window.addEventListener("DOMContentLoaded", generateSnowflakes, false);
window.addEventListener("resize", setResetFlag, false);
}
}
setup();
//
// Конструктор объекта Snowflake
//
function Snowflake(element, speed, xPos, yPos) {
// устанавливаем начальные свойства снежинки
this.element = element;
this.speed = speed;
this.xPos = xPos;
this.yPos = yPos;
this.scale = 1;
// объявляем переменные, используемые для движения снежинки
this.counter = 0;
this.sign = Math.random() < 0.5 ? 1 : -1;
// устанавливаем значения начальной непрозрачности и размера снежинки
this.element.style.opacity = (.1 + Math.random()) / 3;
}
//
// Функция, ответственная за перемещение снежинки
//
Snowflake.prototype.update = function () {
// используем тригонометрию, чтобы определить позицию x и y
this.counter += this.speed / 5000;
this.xPos += this.sign * this.speed * Math.cos(this.counter) / 40;
this.yPos += Math.sin(this.counter) / 40 + this.speed / 30;
this.scale = .5 + Math.abs(10 * Math.cos(this.counter) / 20);
// устанавливаем позицию снежинки
setTransform(Math.round(this.xPos), Math.round(this.yPos), this.scale, this.element);
// если снежинка опустится ниже окна браузера, переместим ее обратно наверх
if (this.yPos > browserHeight) {
this.yPos = -50;
}
}
//
// Эффективный способ установить положение и размер снежинки
//
function setTransform(xPos, yPos, scale, el) {
el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0) scale(${scale}, ${scale})`;
}
//
// Функция, ответственная за создание снежинки
//
function generateSnowflakes() {
// получаем элемент снежинки из DOM и сохраняем его
var originalSnowflake = document.querySelector(".snowflake");
// получаем доступ к контейнеру родителя элемента снежинки
var snowflakeContainer = originalSnowflake.parentNode;
snowflakeContainer.style.display = "block";
// получаем размер окна браузера
browserWidth = document.documentElement.clientWidth;
browserHeight = document.documentElement.clientHeight;
// создаем снежинку
for (var i = 0; i < numberOfSnowflakes; i++) {
// клонируем индивидуальную снежинку и добавляем ее в snowflakeContainer
var snowflakeClone = originalSnowflake.cloneNode(true);
snowflakeContainer.appendChild(snowflakeClone);
// устанавливаем начальную позицию и свойства для оригинальной снежинки
var initialXPos = getPosition(50, browserWidth);
var initialYPos = getPosition(50, browserHeight);
var speed = 5 + Math.random() * 40;
// создаем объект Snowflake
var snowflakeObject = new Snowflake(snowflakeClone,
speed,
initialXPos,
initialYPos);
snowflakes.push(snowflakeObject);
}
// убираем снежинку, так как больше не нужно, чтобы она была видимой
snowflakeContainer.removeChild(originalSnowflake);
moveSnowflakes();
}
//
// Отвечает за перемещение каждой снежинки, вызывая функцию обновления
//
function moveSnowflakes() {
if (enableAnimations) {
for (var i = 0; i < snowflakes.length; i++) {
var snowflake = snowflakes[i];
snowflake.update();
}
}
// Сбрасывает значения позиций всех снежинок до установки нового значения
if (resetPosition) {
browserWidth = document.documentElement.clientWidth;
browserHeight = document.documentElement.clientHeight;
for (var i = 0; i < snowflakes.length; i++) {
var snowflake = snowflakes[i];
snowflake.xPos = getPosition(50, browserWidth);
snowflake.yPos = getPosition(50, browserHeight);
}
resetPosition = false;
}
requestAnimationFrame(moveSnowflakes);
}
//
function getPosition(offset, size) {
return Math.round(-1 * offset + Math.random() * (size + 2 * offset));
}
//
// Сброс позиций всех снежинок
//
function setResetFlag(e) {
resetPosition = true;
}
</script>
После добавления кода проверьте его работоспособность, открыв веб-страницу в браузере. Пример страницы с анимацией.
Цвет нашей снежинки определяется внутри класса .snowflake в CSS:
.snowflake {
position: fixed;
background-color: #CCC;
user-select: none;
z-index: 1000;
pointer-events: none;
border-radius: 50%;
width: 10px;
height: 10px;
}
Измените значение свойства background-color на любой желаемый цвет. В примере установлен #FFFFFF (белый. Если собираетесь использовать этот эффект на более светлом фоне, нужно изменить цвет снежинки на более подходящий.
Чтобы настроить количество снежинок, найдите переменную numberOfSnowflakes в JavaScript-коде:
var numberOfSnowflakes = 50;
Сейчас установлено значение 50. Измените его на большее или меньшее число в зависимости от того, какой эффект вам необходим.
Как работает анимация: обзор кода
Этот эффект работает довольно просто. Позиция и размеры каждой из снежинок (наших элементов) меняются много раз в секунду. Именно эти изменения лежат в основе анимации.
Основа процесса находится в HTML, в котором определен элемент снежинки:

С помощью кода эта единственная снежинка клонируется множеств раз. Количество определяется значением переменной numberOfSnowflakes:

В процессе клонирования снежинок мы вносим незначительные изменения в их внешний вид, чтобы они немного отличались друг от друга:

Мы разберем используемый код в следующих разделах.
Как работает анимация: детальное изучение
Начнем с анализа HTML-кода:
<div id="snowflakeContainer">
<span class="snowflake"></span>
</div>
В разметке задан один элемент с именем snowflakeContainer, и у него есть один дочерний элемент, который относится к классу snowflake. Вот и весь HTML-код, который мы явно указываем в документе.
Отправная точка
В коде JavaScript мы вызываем функцию setup:
В коде JavaScript мы вызываем функцию setup:
function setup() {
if (enableAnimations) {
window.addEventListener("DOMContentLoaded", generateSnowflakes, false);
window.addEventListener("resize", setResetFlag, false);
}
}
setup();
Сначала проверяем, включена ли анимация. После этого нужно прослушать два события. После запуска события DOMContentLoaded нужно вызвать generateSnowflakes. Если размер окна браузера изменяется, вызываем setResetFlag.
Генерируем снежинки
Теперь нам нужно взять одну снежинку, которую мы определили в HTML-коде, и использовать ее в качестве шаблона для множества снежинок. За это отвечает функция generateSnowflakes:
function generateSnowflakes() {
// получаем элемент снежинки из DOM и сохраняем его
var originalSnowflake = document.querySelector(".snowflake");
// получаем доступ к контейнеру родителя элемента снежинки
var snowflakeContainer = originalSnowflake.parentNode;
snowflakeContainer.style.display = "block";
// получаем размер окна браузера
browserWidth = document.documentElement.clientWidth;
browserHeight = document.documentElement.clientHeight;
// создаем каждую индивидуальную снежинку
for (var i = 0; i < numberOfSnowflakes; i++) {
// клонируем индивидуальную снежинку и добавляем ее в snowflakeContainer
var snowflakeClone = originalSnowflake.cloneNode(true);
snowflakeContainer.appendChild(snowflakeClone);
// устанавливаем начальную позицию и свойства для оригинальной снежинки
var initialXPos = getPosition(50, browserWidth);
var initialYPos = getPosition(50, browserHeight);
var speed = 5 + Math.random() * 40;
// создаем объект Snowflake
var snowflakeObject = new Snowflake(snowflakeClone,
speed,
initialXPos,
initialYPos);
snowflakes.push(snowflakeObject);
}
// убираем оригинальную снежинку, так как больше не нужно, чтобы она была видимой
snowflakeContainer.removeChild(originalSnowflake);
moveSnowflakes();
}
Этот код делает следующее:
- Создает клон снежинки в DOM;
- устанавливает случайную начальную позицию снежинки (в зависимости от текущего размера окна браузера);
- устанавливает случайную скорость падения снежинки;
- запускает анимацию.
Все эти шаги повторяются для каждой снежинки, которую мы создаем. Одним из результатов выполнения кода, приведенного ниже, является новый объект Snowflake:
var snowflakeObject = new Snowflake(snowflakeClone,
speed,
initialXPos,
initialYPos);
snowflakes.push(snowflakeObject);
Он не только содержит ссылку на DOM-представление элемента снежинки, но и значения скорости и позиции, которые были сгенерированы несколькими строками ранее:

После создания снежинки мы сохраняем этот объект для последующего использования и «прячем» его в массиве snowflakes. Это происходит внутри цикла. Он выполняется для каждой снежинки.
Последнее, что происходит в функции generateSnowflakes – это вызов метода moveSnowflakes, который отвечает за движение снежинок.
Цикл анимации
В каждой анимации на основе JavaScript, есть цикл анимации. Он отвечает за многократный запуск и содержит код, необходимый для анимирования чего-либо. Функция moveSnowflakes – это и есть цикл анимации:
function moveSnowflakes() {
if (enableAnimations) {
for (var i = 0; i < snowflakes.length; i++) {
var snowflake = snowflakes[i];
snowflake.update();
}
}
// Сбрасывает значения позиций всех снежинок до установки нового значения
if (resetPosition) {
browserWidth = document.documentElement.clientWidth;
browserHeight = document.documentElement.clientHeight;
for (var i = 0; i < snowflakes.length; i++) {
var snowflake = snowflakes[i];
snowflake.xPos = getPosition(50, browserWidth);
snowflake.yPos = getPosition(50, browserHeight);
}
resetPosition = false;
}
requestAnimationFrame(moveSnowflakes);
}
Главное, что происходит в данном цикле анимации – это код для обновления каждой снежинки:
if (enableAnimations) {
for (var i = 0; i < snowflakes.length; i++) {
var snowflake = snowflakes[i];
snowflake.update();
}
}
Мы перебираем каждую снежинку в массиве snowflakes и вызываем метод update.
Реализация Snowflake
Сложность создания анимации снежинки заключается в определении объекта Snowflake. Когда мы впервые создаем снежинки с помощью функции generateSnowflakes, вызывается следующий код:
function Snowflake(element, speed, xPos, yPos) {
// устанавливаем начальные свойства снежинки
this.element = element;
this.speed = speed;
this.xPos = xPos;
this.yPos = yPos;
this.scale = 1;
// объявляем переменные, используемые для движения снежинки
this.counter = 0;
this.sign = Math.random() < 0.5 ? 1 : -1;
// устанавливаем значения начальной непрозрачности и размера снежинки
this.element.style.opacity = (.1 + Math.random()) / 3;
}
После создания снежинки мы сохраняем для нее значения множество свойств. Некоторые свойства мы указываем как аргументы. Но другие свойства, такие как counter, scale и sign, мы определяем для внутреннего использования.
Когда запускается метод update (который вызывает цикл анимации moveSnowflakes), значения свойств изменяются. Метод update выглядит следующим образом:
Snowflake.prototype.update = function () {
// используем тригонометрию, чтобы определить позицию x и y
this.counter += this.speed / 5000;
this.xPos += this.sign * this.speed * Math.cos(this.counter) / 40;
this.yPos += Math.sin(this.counter) / 40 + this.speed / 30;
this.scale = .5 + Math.abs(10 * Math.cos(this.counter) / 20);
// устанавливаем позицию снежинки
setTransform(Math.round(this.xPos), Math.round(this.yPos), this.scale, this.element);
// если снежинка опустится ниже окна браузера, переместим ее обратно наверх
if (this.yPos > browserHeight) {
this.yPos = -50;
}
}
Большая часть этого кода отвечает за то, чтобы снежинки качались во время падения на землю. Это покачивание стало возможным благодаря использованию тригонометрических функций sin и cos. Здесь также используется свойство counter. Оно отвечает за скорость анимации снежинок.
Все эти числовые значения свойств являются частью объекта Snowflake. Но на самом деле они не влияют на DOM-элемент снежинки, который мы видим на экране. Для этого используется функция setTransform:
etTransform(Math.round(this.xPos),
Math.round(this.yPos),
this.scale,
this.element);
Устанавливаем позицию и размер снежинки
Функция setTransform берет все вычисления, выполненные методом update, и переводит их в значения позиции и размера снежинок:
function setTransform(xPos, yPos, scale, el) {
el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0) scale(${scale}, ${scale})`;
}
Мы устанавливаем свойство transform для элемента DOM, который представляет каждую снежинку. А также используем функцию translate3d для настройки горизонтального и вертикального положения.
Также нужно вызвать функцию scale, чтобы настроить размер снежинки во время падения.
Специальные возможности и анимации
Значение свойства enableAnimations сохраняет состояние настройки «Специальные возможности». Есть пользователи, которые при виде такой анимации, как падающий снег, могут почувствовать себя плохо или ощутить тошноту. В большинстве операционных систем есть параметр «Специальные возможности», позволяющий пользователям уменьшить количество анимаций.
Код, где используется enableAnimations, выглядит следующим образом:
// Устанавливаем параметр «Специальные возможности»
var enableAnimations = false;
var reduceMotionQuery = matchMedia("(prefers-reduced-motion)");
// Устанавливаем настройки параметра «Специальные возможности»
function setAccessibilityState() {
if (reduceMotionQuery.matches) {
enableAnimations = false;
} else {
enableAnimations = true;
}
}
setAccessibilityState();
reduceMotionQuery.addListener(setAccessibilityState);
Настройки параметра «Специальные возможности» представлены в системе для уменьшения скорости анимации свойством prefers-reduced-motion.
Устанавливаем анимацию падающего снега в любой контейнер
В описываемом примере снег падает по всей странице. Но при желании можно использовать эффект падающего снега только в определенном контейнере: на изображение, баннер и т.д.
Заключение
Теперь вы увидели, как HTML, CSS и JavaScript объединяются, чтобы создать эффект анимированного падающего снега.