JavaScript | 雪が降るアニメーションサンプル
JavaScriptで指定したHTML要素内に雪を降らせるアニメーションです。
過去にCSSで実装したものを紹介していますが、今回はJavaScriptにてカスタマイズが容易にできるようにしてみました。
関連:CSSのみで雪が降るアニメーションサンプル | ONE NOTES
雪が降るJavaScriptアニメーションの使い方
以下は雪が降るJavaScriptアニメーションの動作サンプルになります。
オプションを指定する事で、雪の量や、速さ、積雪や発光の有無などをカスタマイズができるようにしてあります。
See the Pen JavaScript | Drop Rain Animation by yochans (@yochans) on CodePen.
雪が降るアニメーションのJavaScriptコードは「drop-snow-anim.js」として以下の場所に置いてあります。
ダウンロード:drop-snow-anim.js(v.1.0)
ダウンロードしたjsファイルを読み込む、または既存のJavaScriptコードに貼り付けて利用できます。
<script src="drop-snow-anim.js"></script>
動作サンプルでは以下のHTMLを作成しています。<div>
要素の「container」内に雪を降らせています。
画像のありなしや要素のサイズなど、変更しても対応できているはずです。
<div id="container"></div>
CSSは以下の通りです。
「aspect-ratio」でアスペクト比を固定しています。
#container {
width: 100%;
aspect-ratio: 3 / 1;
}
dropSnowAnim()
という関数を実行する事で、指定したHTML要素に雪を降らせます。
オプションを指定する事でカスタマイズが可能です。
※ 積雪を無効にすると、雪の落下速度が少し早くなります。
//////////////////////////// call function
let option = {
target_element: '#container', // taget HTML element
where_to_insert: 'img', // Insert after the specified element in target_element. If unspecified, insert at the end of the target_element. ('img' 'p' '#id' etc)
snow_amount: 5, // Amount of snow (min 1 max 10)
snow_speed: 5, // Speed of snow (min 1 max 10)
snow_color: '#FFF', // Color of snow (hex, rgba, name)
snow_accumulation: true, // Whether or not there is snow accumulation.
snow_emission_of_light: true, // Emission of light from snow.
animation_time: 600 // Animation time (s)
};
dropSnowAnim(option);
オプション | 内容 | 初期値 |
---|---|---|
target_element | 雪を降らすHTML要素 | body |
where_to_insert | target_element内の指定した要素の次に挿入 未指定の場合はtarget_element内の最後に挿入 ('img’ 'p’ '#id’ etc) | 未指定 |
snow_amount | 雪の量 (min 1 max 10) | 5 |
snow_speed | 雪の速さ (min 1 max 10) | 5 |
snow_color | 雪の色 (hex, rgba , name) | #FFF |
snow_accumulation | 積雪するかどうか | true |
snow_emission_of_light | 雪が発光するかどうか | true |
animation_time | アニメーション時間 (s) | 60 |
- 雪の色を変更できます。
- 量が多く、積雪と発光が有効ですと環境によって重くなる可能性があります。
- アニメーション時間経過後、アニメーションは停止します。指定に制限はありません。
雪が降るアニメーションのJavaScriptコード
以下は雪が降るアニメーションの実行部分のJavaScriptコードになります。
// drop rain animation function
const dropRainAnim = (option) => {
// default option
let default_option = {
target_element: 'body', // taget HTML element
where_to_insert: '', // Insert after the specified element in target_element. If unspecified, insert at the end of the target_element. ('img' 'p' '#id' etc)
rain_amount: 5, // Amount of rain (min 1 max 10)
rain_speed: 5, // Speed of rain (min 1 max 10)
rain_color: '#FFF', // Color of rain (hex, rgba, name)
rain_width: 1, // Width of rain
rain_height: 5, // Length of rain
rain_angle: 15, // Angle of rain (min -90 max 90)
bg_shade: '#000', // Background shade (hex, rgba, name)
bg_darkness: 5, // Adjust background brightness (min 0.0 max 10.0)
splat: false, // Rain splat
lightning: false, // Lightning
lightning_color: '#FFF', // Color of lightning
animation_time: 60 // Animation time (s)
};
// merge option
let op = Object.assign(default_option, option);
// whether the target element exists
if (!document.querySelector(op.target_element)) {
console.log('no target element.');
return;
}
// target element
let target_element = document.querySelector(op.target_element);
target_element.style.position = 'relative';
target_element.style.overflow = 'hidden';
// Insert after the specified element
let insert_after_element = '';
if (op.where_to_insert != '') {
insert_after_element = target_element.querySelector(op.where_to_insert);
}
// main container
let container = document.createElement('div');
if (!insert_after_element) {
target_element.appendChild(container);
} else {
insert_after_element.after(container);
}
container.style.position = 'absolute';
container.style.top = 0;
container.style.left = 0;
container.style.width = '100%';
container.style.height = '100%';
container.style.overflow = 'hidden';
// bg
let container_bg = document.createElement('div');
container.appendChild(container_bg);
container_bg.style.position = 'absolute';
container_bg.style.width = '100%';
container_bg.style.height = '100%';
container_bg.style.backgroundColor = op.bg_shade;
container_bg.style.opacity = op.bg_darkness / 10;
// rain container
let rain_container = document.createElement('div');
container.appendChild(rain_container);
rain_container.style.position = 'absolute';
rain_container.style.width = '100%';
rain_container.style.height = '100%';
rain_container.style.transform = `rotate(${op.rain_angle}deg)`;
// rain clone
let rain = document.createElement('div');
rain.style.position = 'absolute';
rain.style.top = '-100%';
rain.style.left = 0;
rain.style.width = `${op.rain_width}px`;
rain.style.height = `${op.rain_height * 20}px`;
rain.style.opacity = 0.7;
rain.style.backgroundColor = op.rain_color;
// splat container
let splat_container = document.createElement('div');
container.appendChild(splat_container);
splat_container.style.position = 'absolute';
splat_container.style.width = '100%';
splat_container.style.height = '100%';
// splat clone
let splat = document.createElement('div');
splat.style.position = 'absolute';
splat.style.borderRadius = '50%';
splat.style.backgroundColor = op.rain_color;
let count = 0;
op.animation_time *= 60;
const update = () => {
let rand1 = Math.floor(Math.random() * 100);
let rand2 = Math.floor(Math.random() * 100);
if (count % (11 - op.rain_amount) == 0) {
// rain
let rain_clone = rain.cloneNode();
rain_clone.style.left = `${rand1}%`;
rain_container.appendChild(rain_clone);
let rain_anim = rain_clone.animate(
[{ top: `-${op.rain_height * 50}px` }, { top: `${rand2 * 2}%` }],
{
duration: 1400 / op.rain_speed,
iterations: 2
}
);
rain_anim.onfinish = (event) => {
rain_clone.remove();
};
// splat
if (op.splat == true) {
let splat_clone = splat.cloneNode();
splat_clone.style.top = `${rand2 + 0}%`;
splat_clone.style.left = `${rand1}%`;
splat_clone.style.width = `${rand2 + 70}px`;
splat_clone.style.height = `${rand2 / 2}px`;
splat_container.appendChild(splat_clone);
let splat_anim = splat_clone.animate(
[
{ transform: 'scale(0)', opacity: 0 },
{ transform: 'scale(1)', opacity: 0.15 },
{ transform: 'scale(2)', opacity: 0 }
],
{
duration: 500
}
);
splat_anim.onfinish = (event) => {
splat_clone.remove();
};
}
}
// lightning
if (op.lightning == true && rand1 <= 1 && rand2 <= 50) {
container_bg.style.backgroundColor = op.lightning_color;
container_bg.style.opacity = 0.3;
} else if (rand1 <= 80) {
container_bg.style.backgroundColor = op.bg_shade;
container_bg.style.opacity = op.bg_darkness / 10;
}
// stop or run animation
count++;
if (op.animation_time >= count) {
requestAnimationFrame(update);
} else {
cancelAnimationFrame(update);
}
};
update();
};
ディスカッション
コメント一覧
まだ、コメントがありません