雪が降っているような表現をしてみたかったので、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
コメントが承認されるまで時間がかかります。