雪が降っているような表現をしてみたかったので、JavaScriptとCSSを使って揺れながら落下する動きを実装してみました。
サンプルコード
背景全体に表示されるようにしてみます。
HTMLに雪を追加するエリアを記述します。
HTML
<div id="snowarea"></div>
この中にJavaScriptで雪の要素を追加していきます。
CSSで雪のサイズや揺れる動きのパターンを作成します。
基本的な動きは同じで数値を変えるのみなので、Sassのループで作成します。
Sass
#snowarea { position: fixed; top: 0; left: 0; width: 100%; height: 100%; } $snowMinW: 10; // 雪の最小値 $snowPattern: 3; // 雪のサイズパターン $snowStep: 2; // 雪を大きくするステップ @for $i from 0 through $snowPattern { .snow#{$i} { position: absolute; margin-top: -#{($snowMinW + $snowPattern * $snowStep)}px; width: #{$snowMinW + $i * $snowStep}px; height: #{$snowMinW + $i * $snowStep}px; border-radius: #{($snowMinW + $i * $snowStep) / $snowStep}px; background: #ffffff; animation: ease-in-out infinite, linear; box-shadow: 0 0 3px 2px #ffffff; } } $swingMin: 2; // 雪の揺れの最小値 $swingPattern: 15; // 雪の揺れのパターン $swingStep: 1; // 雪の揺れを大きくするステップ @for $i from 0 through $swingPattern { @keyframes snowX#{$i} { 0% { transform: translateX(0px); } 50% { transform: translateX(#{$swingMin + $i * $swingStep}px); } 100% { transform: translateX(0px); } } } @keyframes snowY { 0% { top: 0%; } 100% { top: 100%; margin-top: 0; } }
雪の大きさと動きは最小値とパターン数、いくつずつ大きくしていくかを設定して、ループで設定しています。
雪の動きは左右に揺れる動きと縦に落下する動きの2パターン使っていて、そのうちの左右の動きのみパターンを複数用意しています。
どのパターンを使うかと落下のスピードなどはJavaScriptでランダムに決定します。
JavaScript
var snowPattern = 3; // 雪のサイズパターン(0~3) var swingPattern = 15; // 雪の揺れのパターン(0~15) var addSnowSpeed = 1200; // 雪を追加する間隔 var fallSpeedMin = 40; // 落下スピードの最小(秒速) var fallSpeedMax = 80; // 落下スピードの最大(秒速) var snowarea = document.getElementById('snowarea'); // 雪を追加するエリア var snowArr = []; // 雪を追加する配列 var snowCount = 0; // 雪をいくつ追加したかカウント // 一定時間毎に雪を追加 setInterval(function() { snowArr[snowCount] = createSnow(snowarea); snowarea.appendChild(snowArr[snowCount]); removeSnow(snowarea, snowArr[snowCount]); snowCount++; }, addSnowSpeed); // 雪を作成する関数 function createSnow(area) { // ブラウザの幅と高さを取得 var areaWidth = area.clientWidth; var areaHeight = area.clientHeight; // 雪で使用する各値を取得 var className = 'snow' + getRandomInt(0, snowPattern); var animationName = 'snowX' + getRandomInt(0, swingPattern); var swingSpeed = getRandomInt(1500, 3000) + 'ms'; var fallSpeed = getRandomInt(Math.round(areaHeight / fallSpeedMax), Math.round(areaHeight / fallSpeedMin)) + 's'; var StartPosition = getRandomInt(0, areaWidth) + 'px'; // 雪の作成 var snow = document.createElement('div'); snow.className = className; snow.style.left = ''; snow.setAttribute('style', 'left: ' + StartPosition + '; -webkit-animation-name: ' + animationName + ', snowY; animation-name: ' + animationName + ', snowY; -webkit-animation-duration: ' + swingSpeed + ', ' + fallSpeed + '; animation-duration: ' + swingSpeed + ', ' + fallSpeed + ';'); // 作成した雪を返す return snow; } // 落下し終わった雪を削除する関数 function removeSnow(area, snow) { setTimeout(function() { snow.parentNode.removeChild(snow); }, Math.round(area.clientHeight / fallSpeedMin) * 1000); } // minからmaxまでの乱整数を返す関数 function getRandomInt(min, max) { return Math.floor( Math.random() * (max - min + 1) ) + min; }
これで背景全体に雪が降っているような動きが実装できました。
揺れながら落下するデモページ
サンプルコード2
先ほどのサンプルでは背景全体に表示されるようにしましたが、次はコンテンツ部分に被らないように実装してみます。
雪を追加するエリアをコンテンツの左側用と右側用の2つ用意します。
HTML
<div id="snowareaLeft"></div> <div id="snowareaRight"></div>
左右のエリア用のCSSを記述します。
雪の部分は先ほどと同じなので省略します。
Sass
#snowareaLeft { position: fixed; top: 0; left: 0; height: 100%; } #snowareaRight { position: fixed; top: 0; right: 0; height: 100%; }
JavaScriptのベース部分は同じですが、雪を追加する処理を一つ複製して追加しているのと、リサイズ時に左右のエリアの幅を調整する処理を入れています。
JavaScript
var snowPattern = 3; // 雪のサイズパターン(0~3) var swingPattern = 15; // 雪の揺れのパターン(0~15) var addSnowSpeed = 1500; // 雪を追加する間隔 var fallSpeedMin = 40; // 落下スピードの最小(秒速) var fallSpeedMax = 80; // 落下スピードの最大(秒速) var snowareaLeft = document.getElementById('snowareaLeft'); // 雪を追加するエリア1 var snowArr = []; // 雪を追加する配列 var snowCount = 0; // 雪をいくつ追加したかカウント // 一定時間毎に雪を追加 setInterval(function() { snowArr[snowCount] = createSnow(snowareaLeft); snowareaLeft.appendChild(snowArr[snowCount]); removeSnow(snowareaLeft, snowArr[snowCount]); snowCount++; }, addSnowSpeed); var snowareaRight = document.getElementById('snowareaRight'); // 雪を追加するエリア2 var snowArr2 = []; // 雪を追加する配列 var snowCount2 = 0; // 雪をいくつ追加したかカウント // 一定時間毎に雪を追加 setInterval(function() { snowArr2[snowCount2] = createSnow(snowareaRight); snowareaRight.appendChild(snowArr2[snowCount2]); removeSnow(snowareaRight, snowArr2[snowCount2]); snowCount2++; }, addSnowSpeed); areaWidth(); window.addEventListener('resize', areaWidth); // 雪を降らせるエリアの幅調整をする関数 function areaWidth() { // ブラウザ幅とコンテンツ幅から左右のエリアの幅を決める var windowW = window.innerWidth; var contentsW = document.getElementById('contents').clientWidth; var areaWidth = (windowW - contentsW) / 2; // 表示領域が100pxより広い場合は幅を調整して表示 if(areaWidth > 100) { snowareaLeft.style.display = 'block'; snowareaRight.style.display = 'block'; snowareaLeft.style.width = areaWidth + 'px'; snowareaRight.style.width = areaWidth + 'px'; // 表示領域が100px以下の場合は非表示にする } else { snowareaLeft.style.display = 'none'; snowareaRight.style.display = 'none'; } } // 雪を作成する関数 function createSnow(area) { // ブラウザの幅と高さを取得 var areaWidth = area.clientWidth; var areaHeight = area.clientHeight; // 雪で使用する各値を取得 var className = 'snow' + getRandomInt(0, snowPattern); var animationName = 'snowX' + getRandomInt(0, swingPattern); var swingSpeed = getRandomInt(1500, 3000) + 'ms'; var fallSpeed = getRandomInt(Math.round(areaHeight / fallSpeedMax), Math.round(areaHeight / fallSpeedMin)) + 's'; var StartPosition = getRandomInt(0, areaWidth) + 'px'; // 雪の作成 var snow = document.createElement('div'); snow.className = className; snow.style.left = ''; snow.setAttribute('style', 'left: ' + StartPosition + '; -webkit-animation-name: ' + animationName + ', snowY; animation-name: ' + animationName + ', snowY; -webkit-animation-duration: ' + swingSpeed + ', ' + fallSpeed + '; animation-duration: ' + swingSpeed + ', ' + fallSpeed + ';'); // 作成した雪を返す return snow; } // 落下し終わった雪を削除する関数 function removeSnow(area, snow) { setTimeout(function() { snow.parentNode.removeChild(snow); }, Math.round(area.clientHeight / fallSpeedMin) * 1000); } // minからmaxまでの乱整数を返す関数 function getRandomInt(min, max) { return Math.floor( Math.random() * (max - min + 1) ) + min; }
コンテンツの左右に微妙にかぶることもありますが、概ねコンテンツに被らないように調整ができました。
揺れながら落下するデモページ2
コメントが承認されるまで時間がかかります。