Hero Section with Snowfall Effect Using HTML, CSS, and JavaScript
Designing a beautiful website begins with a killer header that pulls the viewer in and grabs attention the moment they arrive.
In case you are looking for a beautiful and effective tool to share with your audience this magic of winter – welcome to the Hero Section with Snowfall Effect Using HTML, CSS, and JavaScript!
Consider an animation based on the snowfall on a relatively eye-catching hero section — such a small addition would make a big impact.
The best part? I’ll be kind enough to give you the free source code which will make it very easy to integrate this feature.
GitHub Source: Hero Section with Snowfall Effect
Features
- Easy to Customize: Customization of colors and background as well as setting up the snowfall speed is possible through the custom code inserted in the script.
- Responsive: Responsive layout that allows the hero section to look great on desktop screen, tablet and smartphone screen sizes.
- Compatibility: They can be synchronized with the current versions of browsers.
- Clean Code: It follows best practices of coding to a degree that any person with basic programming knowledge can understand the code.
Technologies Used
- HTML (Hypertext Markup Language)
- CSS (Cascading Style Sheets)
- JS (JavaScript)
Recommended for You
- QR Code Scanner or Reader
- QR Code Generator
- Animated Responsive Pricing Table
- 3 Cards Responsive Transparent Pricing Table
- 4 Cards Responsive Pricing Table with Animation
Video Tutorial
Steps to Build Responsive Hero Section
- Create Project Folder: The first step is to create a folder where you will store all the project files.
- Create index.html: Within the folder create an HTML file for the hero section.
- Create style.css: It is important to link a CSS file to style and control the layout of the hero section.
- Create script.js: For the snowfall effect animation it involves using one JavaScript file.
- Copy & Paste the Code: Enjoy the benefits of free source code to finish your project as easy as a pie.
HTML
Here is the HTML code for your index.html file:
<!DOCTYPE html> <!-- Designed by JV Codes at www.jvcodes.com --> <html lang="en" > <head> <meta charset="UTF-8"> <title>Hero Section with Snowfall Effect - JV Codes</title> <link rel="stylesheet" href="./style.css"> </head> <body> <header class="Header__container"> <div class="Header__left"> <svg class="Header__logo" id="logo" y="0px" viewBox="0 0 483 324" xml:space="preserve"> <style type="text/css"> .st0 { fill: #FFFFFF; } </style> <div class="Header__title"> <h1>Bear Cabin</h1> </div> </div> <menu class="Header__menu"> <li class="Header__menuItem">Explore</li> <li class="Header__menuItem">Wander</li> <li class="Header__menuItem">Discover</li> <li class="Header__menuItem">Contact</li> </menu> </header> <main> <section class="Hero"> <div class="Hero__content"> <h2 class="Hero__title">Magic starts here</h2> <p class="Hero__subtitle">Experience a day in a cabin car in the middle of magical Snowfall!</p> <div class="Hero__action" role="button" tabindex=0><span class="Hero__actionText"> Book now</span></div> </div> <div class="Hero__image" id="Hero__image"></div> <div class="Hero__mask"></div> <canvas id="snowfall" class="Hero__snowfall"></canvas> </section> </main> <!-- JavaScript Links --> <script src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js'></script> <script src="./script.js"></script> </body> </html>
CSS
Here is the complete code for style.css file to style the hero section:
@import url("https://fonts.googleapis.com/css?family=Heebo:900|Roboto|Satisfy&display=swap"); body { margin: 0; background-color: #060431; font-family: "Roboto", sans-serif; } .Header__container { box-sizing: border-box; position: fixed; top: 0; left: 0; height: 5em; color: white; display: flex; flex-direction: row; justify-content: space-between; padding: 1em 5vw; width: 100%; } .Header__left { display: flex; flex-direction: row; font-family: "Satisfy", cursive; } .Header__logo { width: 4.5em; margin: auto; } .Header__title { display: flex; margin-left: 0.6em; } h1 { font-weight: normal; margin: 0.1em; } .Header__menu { margin: auto 0; display: flex; flex-direction: row; text-transform: uppercase; font-size: 0.8em; } .Header__menuItem { list-style-type: none; margin: auto 1em; } .Hero { color: white; margin: 0; width: 100%; height: 100vh; display: flex; overflow: hidden; justify-content: center; } .Hero__content { margin: 40vh auto auto 10vw; width: 50%; } .Hero__title { font-family: "Heebo", sans-serif; font-weight: 900; margin: 0; text-shadow: 0px 0px 8px black; font-size: 3em; line-height: 1em; } .Hero__subtitle { opacity: 0.85; margin-top: 0.5em; text-shadow: 0px 0px 8px black; font-size: 1.5em; } .Hero__action { height: 2em; border-radius: 2em; background-color: #c49000; color: white; border: none; padding: 0.4em 2em; box-shadow: 0px 0px 8px black; position: relative; width: 7em; cursor: pointer; } @media (max-width: 700px) { .Hero__content { width: 90%; margin: 40vh 2em auto 2em; } } .Hero__action:hover { animation: callToAction 1500ms ease-in infinite; } @keyframes callToAction { 0% { transform: scale(1); } 40% { transform: scale(1.07); } 50% { transform: scale(1.04); } 60% { transform: scale(1.07); } 100% { transform: scale(1); } } .Hero__actionText { position: absolute; top: 50%; left: 50%; z-index: 2; font-family: "Heebo", sans-serif; font-size: 1.1em; transform: translate(-50%, -50%); text-shadow: 0px 0px 6px rgba(255, 255, 255, 0.2); } @media (max-height: 500px) { .Hero__title { font-size: 2em; } .Hero__subtitle { font-size: 1em; } .Hero__action { height: 1.4em; border-radius: 1.4em; width: 4.5em; } .Hero__actionText { font-size: 0.9em; } } .Hero__image { display: inline-block; position: absolute; z-index: -10; height: 100vh; width: 100%; background-color: red; background-image: url("/resources/snowfall1.webp"); background-position: center; background-size: cover; } .Hero__mask { position: absolute; z-index: -8; height: 100%; width: 100%; background-image: linear-gradient(#180047, #060231 50%); mix-blend-mode: multiply; opacity: 0.5; } .Hero__snowfall { position: absolute; width: 100%; height: 100%; z-index: -6; opacity: 0.75; }
JavaScript
Here is the complete code for script.js file to function the hero section:
const snowFall = (() => { // Constants // Quantity of snowflakes generated in average each second on each 100px section const FLAKE_FREQUENCY = 7; //7 // Speed of the slowest snowflake (px/sec) before wind noise const FLAKE_MIN_SPEED = 30; // Speed of the fastest snowflake (px/sec) before wind noise const FLAKE_MAX_SPEED = 180; // Noise applicated to the flakes sizes const FLAKE_SIZE_NOISE = 0.5; // Radius of the furthest snowflake in px before size noise const FLAKE_MIN_SIZE = 0.4; // Radius of the nearest snowflake in px before size noise const FLAKE_MAX_SIZE = 1.6; // Friction coefficient applicated by the air to the flakes [0; 1] const FLAKE_FRICTION = 0.035; // Maximum force applicated to the flakes to stray from x axis const FLAKE_NOISE_X = 0.07; // Maximum force applicated to the flakes to stray from y axis const FLAKE_NOISE_Y = 0.02; // Radius of influence of the mouse move (ratio of the width or height of the screen) const FLAKE_CAST_RADIUS_RATIO = 0.4; // Force applicated to the flakes on mouse mouve const FLAKE_CAST_FORCE = 0.05; // Minimum depth to trigger a cast on mouse move const FLAKE_CAST_DEPTH_TRIGGER = 0.4; // Delay in ms between two consecutive snow casts const FLAKE_CAST_THROTTLE = 50; // Useful shortcut const PI = Math.PI; // Estimated framerate const FPS = 60; // Objects class Point { static distance(a, b) { return Math.sqrt(Math.pow(b.y - a.y, 2) + Math.pow(b.x - a.x, 2)); } constructor(x = 0, y = 0) { this.x = x; this.y = y; } translate(translateVect) { this.x += translateVect.x; this.y += translateVect.y; } } class Vector { static add(vectors) { let result = new Vector(0, 0); vectors.forEach((vector) => { result.x += vector.x; result.y += vector.y; }); return result; } constructor(x = 0, y = 0) { this.x = x; this.y = y; } get length() { return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2)); } } class Particle { static deduceTargetSpeed(mass, friction) { return new Vector(0, mass / FLAKE_FRICTION); } static deduceMass(targetSpeed, friction) { return targetSpeed.y * FLAKE_FRICTION; } constructor( position = { x: 0, y: 0 }, { mass = 0, friction = 0, initialSpeed = { x: 0, y: 0 } } ) { this.position = new Point(position.x, position.y); this.mass = mass; this.friction = friction; this.speed = new Vector(initialSpeed.x, initialSpeed.y); this.forces = new Map(); this.applyPhysics(); } setForce(forceName, forceValue = { x: 0, y: 0 }) { this.forces.set(forceName, new Vector(forceValue.x, forceValue.y)); } applyGravity() { this.setForce("weight", { x: 0, y: this.mass }); } applyFriction() { this.setForce("friction", { x: -this.speed.x * this.friction, y: -this.speed.y * this.friction }); } applyPhysics() { if (this.mass) this.applyGravity(); if (this.friction) this.applyFriction(); const acceleration = Vector.add(this.forces); this.forces.clear(); this.speed = Vector.add([this.speed, acceleration]); this.position.translate(this.speed); } } class Flake extends Particle { constructor(position = { x: 0, y: 0 }) { const depth = random(0, 100) / 100; const initialSpeed = { x: 0, y: (FLAKE_MIN_SPEED + depth * (FLAKE_MAX_SPEED - FLAKE_MIN_SPEED)) / FPS }; const mass = Particle.deduceMass(initialSpeed, FLAKE_FRICTION); super(position, { mass: mass, friction: FLAKE_FRICTION, initialSpeed: { x: initialSpeed.x, y: initialSpeed.y } }); this.depth = depth; this.size = FLAKE_MIN_SIZE + depth * (FLAKE_MAX_SIZE - FLAKE_MIN_SIZE); this.size = this.size * (1 + FLAKE_SIZE_NOISE * (random(-100, 100) / 100)); this.noiseSpeed = new Vector(0, 0); } evolve() { if (this.depth < FLAKE_CAST_DEPTH_TRIGGER && this.forces.get("cast")) this.forces.delete("cast"); this.applyPhysics(); this.addNoise(); } addNoise() { this.noiseForce = new Vector( (random(-100, 100) / 100) * FLAKE_NOISE_X * this.depth, (random(-100, 100) / 100) * FLAKE_NOISE_Y * this.depth ); this.noiseSpeed = Vector.add([this.noiseSpeed, this.noiseForce]); this.position.translate(this.noiseSpeed); } draw(ctx) { ctx.beginPath(); ctx.arc(this.position.x, this.position.y, this.size, 0, 2 * PI); ctx.fillStyle = "white"; ctx.fill(); } } class ForceField { constructor(forceName, initialWidth, initialHeight) { this.forceName = forceName; this.reset(initialWidth, initialHeight); } reset(l, h) { const targetWidth = l ? l : this.width; const targetHeight = h ? h : this.height; this.forces = Array(targetWidth) .fill(null) .map(() => Array(targetHeight).fill(null)); this.isEmpty = true; this.width = targetWidth; this.height = targetHeight; } getForceAt(n, m) { if (this.isEmpty) return; let force; try { force = this.forces[n][m]; } catch { return; } if (!force) return; if (!("x" in force && "y" in force)) { console.warn( `${this.forceName} : The value found at ${n} ${m} is not a force` ); return; } return { x: force.x, y: force.y }; } setForceAt(n, m, value) { let force; try { force = Object.assign({}, { x: value.x, y: value.y }); } catch { console.warn( `${this.forceName} : The value provoded to be set at ${n} ${m} is not a force` ); } try { this.forces[n][m] = Object.assign({}, force); } catch { console.warn(`${this.forceName} : Failed to set ${n} ${m} force`); return; } this.isEmpty = false; } applyField(particles) { if (this.isEmpty) return; particles.forEach((particle, index) => { if (!(particle instanceof Particle)) { console.warn( `The ${index}th Object provided to the ${this.forceName} force is not a particle` ); return; } const x = Math.round(particle.position.x); const y = Math.round(particle.position.y); const force = this.getForceAt(x, y); if (force) particle.setForce(this.forceName, force); }); this.reset(); } } class FlakeCaster extends ForceField { calculateField(x, y, force, castRadius) { const boundary = Math.ceil(castRadius / 2); const iMin = x - boundary > 0 ? x - boundary : 0; const iMax = x + boundary < this.width ? x + boundary : this.width; const jMin = y - boundary > 0 ? y - boundary : 0; const jMax = y + boundary < this.height ? y + boundary : this.height; for (let i = iMin; i < iMax; i++) { for (let j = jMin; j < jMax; j++) { const distanceToEpicenter = Point.distance( { x: x, y: y }, { x: i, y: j } ); let intensity = FLAKE_CAST_FORCE * (1 - distanceToEpicenter / boundary); intensity = intensity < 0 ? 0 : intensity; this.setForceAt(i, j, { x: force.x * intensity, y: force.y * intensity }); } } } } // Utils function random(min, max) { const range = Math.abs(max) + Math.abs(min); return Math.round(Math.random() * range + min); } function chance(probability) { return Math.random() < probability ? true : false; } // Main instructions this.init = function () { const canvas = document.getElementById("snowfall"); const ctx = canvas.getContext("2d"); let { width, height } = canvas; const flakes = []; let flakeRequestPerFrame; let castRadius; const castField = new FlakeCaster("cast", width, height); const mouse = { currentPosition: { x: 0, y: 0 }, prevPosition: { x: 0, y: 0 }, move: function (x, y) { this.prevPosition = Object.assign({}, this.currentPosition); this.currentPosition.x = x; this.currentPosition.y = y; }, get vitesse() { return { x: this.currentPosition.x - this.prevPosition.x, y: this.currentPosition.y - this.prevPosition.y }; } }; function resize() { const { innerWidth, innerHeight } = window; canvas.width = innerWidth; canvas.height = innerHeight; width = innerWidth; height = innerHeight; castRadius = FLAKE_CAST_RADIUS_RATIO * Math.min(width, height); flakeRequestPerFrame = ((width / 100) * FLAKE_FREQUENCY) / FPS; castField.reset(width, height); } function castFlakes(e) { let x, y; if (e.type === "mousemove") { x = e.clientX; y = e.clientY; } if (e.type === "touchmove") { x = e.touches[0].clientX; y = e.touches[0].clientY; } mouse.move(x, y); if (mouse.vitesse.x && mouse.vitesse.y) { const force = Object.assign( {}, { x: mouse.vitesse.x, y: mouse.vitesse.y } ); castField.calculateField(x, y, force, castRadius); } } function draw() { ctx.clearRect(0, 0, width, height); castField.applyField(flakes); let nbFlakeToCreate = Math.floor(flakeRequestPerFrame); if (chance(flakeRequestPerFrame % 1)) nbFlakeToCreate++; for (let i = 0; i < nbFlakeToCreate; i++) { flakes.push(new Flake({ x: random(0, width), y: 0 })); } flakes.forEach((flake, index, flakes) => { if ( !( _.inRange(flake.position.y, height) && _.inRange(flake.position.x, width) ) ) { flakes.splice(index, 1); } flake.evolve(); flake.draw(ctx); }); window.requestAnimationFrame(draw); } resize(); window.onresize = resize; window.onmousemove = _.throttle(castFlakes, FLAKE_CAST_THROTTLE); window.addEventListener( "touchmove", _.throttle(castFlakes, FLAKE_CAST_THROTTLE) ); draw(); }; return this; })(); document.addEventListener("DOMContentLoaded", snowFall.init);
Download Source Code
Below is the button link to download all the source codes for this Hero Section with Snowfall Effect. It is free for use in your projects or even your websites.
Conclusion
The Hero Section with Snowfall Effect Using HTML, CSS, and JavaScript is one of the ways of creating that magical part for your website. Feel free to use this code in your projects, and remember to link back to JV Source Codes for a backlink and for the record.
If you experience any issues with this code, please write a comment below.