JavaScript | 雨が降るアニメーションサンプル
JavaScriptで指定したHTML要素内に雨を降らせるアニメーションです。
以前、CSSで実装したものに、新しい要素を加えてJavaScriptで作り直してみました。
関連:CSSのみで雨が降るアニメーションサンプル | ONE NOTES
雨が降るJavaScriptアニメーションの使い方
以下は雨が降るJavaScriptアニメーションの動作サンプルになります。
オプションを指定する事で、雨の量や、速さ、角度、サイズ、色、雨脚や雷光などをカスタマイズができるようにしてあります。
See the Pen JavaScript by yochans (@yochans) on CodePen.
雨が降るアニメーションのJavaScriptコードは「DropRainAnim.js」として以下の場所に置いてあります。
ダウンロード:drop-rain-anim.js(v.1.0)
ダウンロードしたjsファイルを読み込む、または既存のJavaScriptコードに貼り付けて利用できます。
<script src="drop-rain-anim.js"></script>
動作サンプルでは以下のHTMLを作成しています。<div>
要素の「container」内に雨を降らせています。
画像のありなしや要素のサイズなど、変更しても対応できているはずです。
<div id="container"></div>
CSSは以下の通りです。
「aspect-ratio」でアスペクト比を固定しています。
#container {
width: 100%;
aspect-ratio: 3 / 1;
}
dropRainAnim()
という関数を実行する事で、指定したHTML要素に雨を降らせます。
オプションを指定する事でカスタマイズが可能です。
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)
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: true, // Rain splat
lightning: true, // Lightning
lightning_color: '#FFF', // Color of lightning
animation_time: 60 // Animation time (s)
};
dropRainAnim(option);
dropRainAnim()
で利用可能なオプション設定です。
オプションの指定がないものはデフォルト値が割り当てられます。
オプション | 内容 | 初期値 |
---|---|---|
target_element | 雨を降らすHTML要素 | body |
where_to_insert | target_element内の指定した要素の次に挿入 未指定の場合はtarget_element内の最後に挿入 ('img’ 'p’ '#id’ etc) | 未指定 |
rain_amount | 雨の量 (min 1 max 10) | 5 |
rain_speed | 雨の速さ (min 1 max 10) | 5 |
rain_color | 雨の色 (hex, rgba , name) | #FFF |
rain_width | 雨の太さ (1 = 1px) | 1 |
rain_height | 雨の長さ(1 = 20px) | 5 |
rain_angle | 雨の角度 (min -90 max 90) | 15 |
bg_shade | 背景の色合い (hex, rgba , name) | #000 |
bg_darkness | 背景の明るさ調整 (min 0.0 max 10.0) | 5 |
splat | 雨脚 | false |
lightning | 雷光 | false |
lightning_color | 雷光の色 (hex, rgba , name) | #FFF |
animation_time | アニメーション時間 (s) | 60 |
雨が降るアニメーションのJavaScriptコード
以下は雨が降るアニメーションの実行部分のJavaScriptコードになります。
対象のHTML要素が見つからない場合は停止します。
// 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();
};
ディスカッション
コメント一覧
まだ、コメントがありません