Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating Falling Snow using HTML 5 and JS

I visited the Stack Exchange Winter Bash website and I love the falling snow! My question is, how can I recreate a similar effect that looks as nice. I attempted to reverse engineer the code to see if I could figure it out but alas no luck there. The JS is over my head. I did a bit of googling and came across some examples but they were not as elegant as the SE site or did not look very good.

Can anyone provide some instructions on how to replicate what the SE Winter Bash site creates or a place where I might learn how to do this?

Edit: I would like to replicate the effect as close as possible, IE: falling snow with snowflakes, and being able to move the mouse and cause the snow to move or swirl with the mouse moments.

like image 739
L84 Avatar asked Dec 21 '12 03:12

L84


2 Answers

Great question, I actually wrote a snow plugin a while ago that I continually update see it in action. Also a link to the pure js source

I noticed you tagged the question html5 and canvas, however you can do it without using either, and just standard elements with images or different background colors.

Here's two really simple ones I put together just now for you to mess with. The key in my opinion is using sin to get the nice wavy effect as the flakes fall. The first one uses the canvas element, the 2nd one uses regular dom elements.

Since I'm absolutely addicted to canvas here's a canvas version that performs quite nicely in my opinion.

Canvas version

Full Screen

(function() {     var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame ||     function(callback) {         window.setTimeout(callback, 1000 / 60);     };     window.requestAnimationFrame = requestAnimationFrame; })();   var flakes = [],     canvas = document.getElementById("canvas"),     ctx = canvas.getContext("2d"),     flakeCount = 200,     mX = -100,     mY = -100      canvas.width = window.innerWidth; canvas.height = window.innerHeight;  function snow() {     ctx.clearRect(0, 0, canvas.width, canvas.height);      for (var i = 0; i < flakeCount; i++) {         var flake = flakes[i],             x = mX,             y = mY,             minDist = 150,             x2 = flake.x,             y2 = flake.y;          var dist = Math.sqrt((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y)),             dx = x2 - x,             dy = y2 - y;          if (dist < minDist) {             var force = minDist / (dist * dist),                 xcomp = (x - x2) / dist,                 ycomp = (y - y2) / dist,                 deltaV = force / 2;              flake.velX -= deltaV * xcomp;             flake.velY -= deltaV * ycomp;          } else {             flake.velX *= .98;             if (flake.velY <= flake.speed) {                 flake.velY = flake.speed             }             flake.velX += Math.cos(flake.step += .05) * flake.stepSize;         }          ctx.fillStyle = "rgba(255,255,255," + flake.opacity + ")";         flake.y += flake.velY;         flake.x += flake.velX;          if (flake.y >= canvas.height || flake.y <= 0) {             reset(flake);         }           if (flake.x >= canvas.width || flake.x <= 0) {             reset(flake);         }          ctx.beginPath();         ctx.arc(flake.x, flake.y, flake.size, 0, Math.PI * 2);         ctx.fill();     }     requestAnimationFrame(snow); };  function reset(flake) {     flake.x = Math.floor(Math.random() * canvas.width);     flake.y = 0;     flake.size = (Math.random() * 3) + 2;     flake.speed = (Math.random() * 1) + 0.5;     flake.velY = flake.speed;     flake.velX = 0;     flake.opacity = (Math.random() * 0.5) + 0.3; }  function init() {     for (var i = 0; i < flakeCount; i++) {         var x = Math.floor(Math.random() * canvas.width),             y = Math.floor(Math.random() * canvas.height),             size = (Math.random() * 3) + 2,             speed = (Math.random() * 1) + 0.5,             opacity = (Math.random() * 0.5) + 0.3;          flakes.push({             speed: speed,             velY: speed,             velX: 0,             x: x,             y: y,             size: size,             stepSize: (Math.random()) / 30,             step: 0,             angle: 180,             opacity: opacity         });     }      snow(); };  canvas.addEventListener("mousemove", function(e) {     mX = e.clientX,     mY = e.clientY });  init();​ 

Standard element version

var flakes = [],     bodyHeight = getDocHeight(),     bodyWidth = document.body.offsetWidth;   function snow() {     for (var i = 0; i < 50; i++) {         var flake = flakes[i];          flake.y += flake.velY;          if (flake.y > bodyHeight - (flake.size + 6)) {             flake.y = 0;         }          flake.el.style.top = flake.y + 'px';         flake.el.style.left = ~~flake.x + 'px';          flake.step += flake.stepSize;         flake.velX = Math.cos(flake.step);          flake.x += flake.velX;          if (flake.x > bodyWidth - 40 || flake.x < 30) {             flake.y = 0;         }     }     setTimeout(snow, 10); };   function init() {     var docFrag = document.createDocumentFragment();     for (var i = 0; i < 50; i++) {         var flake = document.createElement("div"),             x = Math.floor(Math.random() * bodyWidth),             y = Math.floor(Math.random() * bodyHeight),             size = (Math.random() * 5) + 2,             speed = (Math.random() * 1) + 0.5;          flake.style.width = size + 'px';         flake.style.height = size + 'px';         flake.style.background = "#fff";          flake.style.left = x + 'px';         flake.style.top = y;         flake.classList.add("flake");          flakes.push({             el: flake,             speed: speed,             velY: speed,             velX: 0,             x: x,             y: y,             size: 2,             stepSize: (Math.random() * 5) / 100,             step: 0         });         docFrag.appendChild(flake);     }      document.body.appendChild(docFrag);     snow(); };  document.addEventListener("mousemove", function(e) {     var x = e.clientX,         y = e.clientY,         minDist = 150;      for (var i = 0; i < flakes.length; i++) {         var x2 = flakes[i].x,             y2 = flakes[i].y;          var dist = Math.sqrt((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y));          if (dist < minDist) {             rad = Math.atan2(y2, x2), angle = rad / Math.PI * 180;              flakes[i].velX = (x2 / dist) * 0.2;             flakes[i].velY = (y2 / dist) * 0.2;              flakes[i].x += flakes[i].velX;             flakes[i].y += flakes[i].velY;         } else {             flakes[i].velY *= 0.9;             flakes[i].velX             if (flakes[i].velY <= flakes[i].speed) {                 flakes[i].velY = flakes[i].speed;             }         }     } });  init();  function getDocHeight() {     return Math.max(     Math.max(document.body.scrollHeight, document.documentElement.scrollHeight), Math.max(document.body.offsetHeight, document.documentElement.offsetHeight), Math.max(document.body.clientHeight, document.documentElement.clientHeight)); }​ 
like image 119
Loktar Avatar answered Sep 23 '22 11:09

Loktar


I've created a pure HTML 5 and js snowfall. Check it out on my code pen here: https://codepen.io/onlintool24/pen/GRMOBVo

// Amount of Snowflakes var snowMax = 35;  // Snowflake Colours var snowColor = ["#DDD", "#EEE"];  // Snow Entity var snowEntity = "&#x2022;";  // Falling Velocity var snowSpeed = 0.75;  // Minimum Flake Size var snowMinSize = 8;  // Maximum Flake Size var snowMaxSize = 24;  // Refresh Rate (in milliseconds) var snowRefresh = 50;  // Additional Styles var snowStyles = "cursor: default; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; -o-user-select: none; user-select: none;";  /* // End of Configuration // ---------------------------------------- // Do not modify the code below this line */  var snow = [],     pos = [],     coords = [],     lefr = [],     marginBottom,     marginRight;  function randomise(range) {     rand = Math.floor(range * Math.random());     return rand; }  function initSnow() {     var snowSize = snowMaxSize - snowMinSize;     marginBottom = document.body.scrollHeight - 5;     marginRight = document.body.clientWidth - 15;      for (i = 0; i <= snowMax; i++) {         coords[i] = 0;         lefr[i] = Math.random() * 15;         pos[i] = 0.03 + Math.random() / 10;         snow[i] = document.getElementById("flake" + i);         snow[i].style.fontFamily = "inherit";         snow[i].size = randomise(snowSize) + snowMinSize;         snow[i].style.fontSize = snow[i].size + "px";         snow[i].style.color = snowColor[randomise(snowColor.length)];         snow[i].style.zIndex = 1000;         snow[i].sink = snowSpeed * snow[i].size / 5;         snow[i].posX = randomise(marginRight - snow[i].size);         snow[i].posY = randomise(2 * marginBottom - marginBottom - 2 * snow[i].size);         snow[i].style.left = snow[i].posX + "px";         snow[i].style.top = snow[i].posY + "px";     }      moveSnow(); }  function resize() {     marginBottom = document.body.scrollHeight - 5;     marginRight = document.body.clientWidth - 15; }  function moveSnow() {     for (i = 0; i <= snowMax; i++) {         coords[i] += pos[i];         snow[i].posY += snow[i].sink;         snow[i].style.left = snow[i].posX + lefr[i] * Math.sin(coords[i]) + "px";         snow[i].style.top = snow[i].posY + "px";          if (snow[i].posY >= marginBottom - 2 * snow[i].size || parseInt(snow[i].style.left) > (marginRight - 3 * lefr[i])) {             snow[i].posX = randomise(marginRight - snow[i].size);             snow[i].posY = 0;         }     }      setTimeout("moveSnow()", snowRefresh); }  for (i = 0; i <= snowMax; i++) {     document.write("<span id='flake" + i + "' style='" + snowStyles + "position:absolute;top:-" + snowMaxSize + "'>" + snowEntity + "</span>"); }  window.addEventListener('resize', resize); window.addEventListener('load', initSnow);
body{   background: skyblue;   height:100%;   width:100%;     display:block;   position:relative; }
<span id="flake0" style="cursor: default; user-select: none; position: absolute; font-family: inherit; font-size: 19px; color: rgb(221, 221, 221); z-index: 1000; left: 226px; top: 561px;">•</span>
like image 36
Online Tool Avatar answered Sep 20 '22 11:09

Online Tool