Один из наших клиентов попросил поставить новогоднюю заставку на сайт. Польза — рассказать клиентам, что до конца праздников они не работают и вернутся только с 11-го января. Ну а заодно, повеселить пользователей новогодним видом сайта. Для этого мы сделали гифку с новогодними игрушками и снегопадом.
Пока ставили все это на сайт стали обсуждать с коллегами, что прикольно было бы на весь сайт пустить снег. В интернете нашлись уже разные исполнения нашей задумки, но на боевой проект их бездумно ставить страшно. Во-первых, многие тормозят, ведь не у всех клиентов быстрый интернет и современные браузеры. Во-вторых, ставить чужие скрипты опасно в принципе.
В общем, не хочу больше затягивать, я за пол часика собрал наше «легкое» исполнение. Идею подсмотрел, но убрал все лишнее и сделал под себя. Хочу им поделится с читателями. Почитать будет полезно тем, кто только изучает джаваскрипт. А итог могут использовать на своих проектах все желающие. Так же буду рад критике коллег.
Результат
Процесс
Эта часть для тех, кто только изучает джаваскрипт. Всем остальным, думаю, будет не очень интересно.
Все, что нам понадобится в HTML, это canvas с идентефикатором snow. И я запустил скрипт по событию на BODY onload=“init()”. Это не лучшее решение, но оно за рамками самого «снега», так что оставлю его на совесть того, кто будет код использовать.
Весь наш код состоит из четырех функций.
function init() — тут мы делаем подготовку к запуску и запускаем. В этой функции должны быть все операции, которые нужно произвести до основного кода.
function draw() — отвечает за отрисовку текущего состояния снега.
function update() — отвечает за установку нового состояния. Оно будет отрисовано на следующем шаге.
function mousemove(event) — отвечает за отслеживание положения мыши. Эту функцию я добавил по тому, что захотелось какого-то интерактива. Снег не должен всегда падать только вниз, в жизни его сдувает ветром то туда, то сюда. Я решил, что за ветер будет отвечать курсор. Чем дальше он от центра экрана (горизонтално), тем сильнее ветер в эту сторону. Если интерактив не нужен, то от этой части можно отказаться.
Подробнее
Начну разбор с инициализации. Для начала я накидал что-то подобное:
function init() {
//Canvas init
var canvas = document.getElementById(‘snow’);
//Canvas resize
var W = window.innerWidth;
var H = window.innerHeight;
canvas.width = W;
canvas.height = H;
//Get context
var ctx = canvas.getContext(‘2d’);
ctx.strokeStyle = ‘white’;
//Prepare array of particles
var mp = 100;
var particles = [];
for (var i = 0; i < mp; i++) {
particles.push({
x: Math.random() * W,
y: Math.random() * H,
r: Math.random() * 3 + 1
})
}
}
Первые три блока можно скопировать из любого «Самоучителя по Canvas для чайников». Получаем конвас, растягиваем на всю страницу и готовимся рисовать белым цветом.
В последнем блоке создаем массив, который будет отвечать за снежинки. Particles — массив снежинок. А каждая снежинка это объект, который «помнит» о своих координатах и диаметре. Для каждой снежинки задаем её начальное состояние: она должна быть где-то на нашем канвасе (случайным образом) и должна получить диаметр от 1 до 4 пикселей.
После этого переходим к отрисовке текущего состояния нашего снега.
function draw(W, H, particles, mp, ctx) {
//Clearing
ctx.clearRect(0, 0, W, H);
//Set white whith opacity 0.8
ctx.fillStyle = “rgba(255, 255, 255, 0.8)”;
//Drowing
ctx.beginPath();
for (var i = 0; i < mp; i++) {
var p = particles[i];
ctx.moveTo(p.x, p.y);
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2, true);
}
ctx.fill();
}
Тут то же все просто. Чистим поле и заново рисуем каждую снежинку на её координатах и с её диаметром. Для новичков хочу обратить нимание, что мы сначала «накидываем» все в контакст. И только один раз отрисовываем с помощью ctx.fill();
Переходим к анимации. Пишем функцию, которая будет смещать все снежинки вниз. Если их скорость будет одинаковой будет скучно. По этому мы представляем, что большие снежинки ближе, потому и падают быстрее.
function update(W, H, particles, mp, ctx) {
for (var i = 0; i < mp; i++) {
var p = particles[i];
p.y += 1 + p.r/2;
if (p.y > H) {
particles[i] = {
x: Math.random() * W,
y: -10,
r: p.r
};
}
}
}
Скорость падения зависит от радиуса. Можно добавить какой-нибудь дополнительный случайный параметр, который вместо радиуса будет влиять на скорость. Но мне показалось, что и так не плохо. Если снежинка вылетела за поле вниз, то сразу перемещаем её в выше верха и смещаем случайно по оси X. Ничего хитрого.
Когда мы все это подготовили нам осталось запустить отрисовку по таймеру. Для этого добавляем в init:
setInterval(function() {
draw(W, H, particles, mp, ctx);
}, 50);
И после отрисовки сразу общитывать новое состояние. Для этого вызываем update в конце draw:
update(W, H, particles, mp, ctx);
Интерактивчик
Все. Простой снег готов. Но мне захотелось добавить интерактива. А то никакого профита, что налету снег обсчитываем. Добавляем функцию, которая отслеживает движения мыши и всгда записывает её положение. Для записи я расширил объект body, который точно есть у нашей веб-страницы. Добавляем в init:
document.body.myData = {
x: 0,
y: 0
};
document.onmousemove = mousemove;
Теперь по умолчанию мышка у нас в верхнем левом углу, но как только пользователь её сдвинет, мы обновим координаты.
Записывать при сдвиге мыши будем с помощью такой функции:
function mousemove(event) {
var mouse_x = mouse_y = 0;
if (document.attachEvent != null) {
mouse_x = window.event.clientX;
mouse_y = window.event.clientY;
} else if (!document.attachEvent && document.addEventListener) {
mouse_x = event.clientX;
mouse_y = event.clientY;
}
document.body.myData.x = mouse_x;
document.body.myData.y = mouse_y;
}
Отследить то мы отследили. Осталось добавить скорость вдоль горизонтальной оси Ox:
var speed_x = (document.body.myData.x – (W / 2)) / W;
Смотрим в какой половине мышь: в правой или левой. И как далеко она от центра. Чем дальше, тем «сильнее ветер».
И осталось добавить сдвиг по Ox:
p.x += speed_x * 10;
Вместо послесловия
Приглашаю уважаемых коллег критиковать мой код и предлагать, как было бы сделать лучше. А новичков задавать вопросы.