🎃

複数のボールが自由落下するアニメーション

2024/08/09に公開

HTML:class-itemは自由落下するボールを表しています。現在は6個表示していますが、コピペし任意の数に変更できます。

falldowns.html
<div id="stage">
    <div class="item"></div>	
    <div class="item"></div>	
    <div class="item"></div>	
    <div class="item"></div>
    <div class="item"></div>	
    <div class="item"></div>
</div>
falldowns.css
#stage {
	position:relative;
	border:1px solid #666;
	width:100%;
	height:450px;
}
/* 移動するアイテム */
.item {
	position:absolute;
	margin:0 auto;
	border-radius:50%;
}
falldowns.js
var gravity = 1.8,  
  v0 = 0,
  y0 = {},
  y={},
  v={},
  x={},
  baseY={},
  sid={},
  b = {},//輝度
  size = {},
  damping = 0.7,  // 反発係数(減衰)
  time = 0.03, // 経過時間(秒)
  itemElem  = document.querySelectorAll('.item'),//動作対象
  stageElem = document.getElementById('stage');//監視対象
  let options = {threshold:0.1};

for (let i = 0; i < itemElem.length; i++) {
	y[i] =  Math.random()* 100; //初期位置
	x[i] =  Math.random()* 300;
	y0[i] = y[i];
	v[i] =0 ; //初期速度
	b[i] = Math.random()*360;//色相
	size[i] = Math.random()*15 + 25;
	baseY[i] = stageElem.clientHeight - itemElem[i].clientHeight; //着地地点
}
// 位置を算出
var calcPosition = function() {
	for (let i = 0; i < itemElem.length; i++) {
	v[i] += v0 + (gravity * time);	// 速度v
	y[i] += v[i]* time ;	// 位置y
	if (y[i] > baseY[i]) {
		y[i] = baseY[i];//着地地点
		v[i] *= -damping;//バウンド動作
	}}};

var updatePosition = function() {//アイテム要素の位置を更新
	for (let i = 0; i < itemElem.length; i++) {
	itemElem[i].style.top = y[i] -  size[i] + 'px';
	itemElem[i].style.left = x[i] + 'px';
	}
};
updatePosition();// 初期位置

//監視
const io = new IntersectionObserver(callback,options);
	io.observe(stageElem);

// アニメーション
function callback(entries){
 if(entries[0].isIntersecting) {
	for (let i = 0; i < itemElem.length; i++) {
	sid[i] = setInterval(function() {
		itemElem[i].style.backgroundColor = "hsl("+b[i]+",45%,70%,0.5)";//色
		itemElem[i].style.width = size[i] + "px";
		itemElem[i].style.height = size[i] + "px";
		calcPosition();
		updatePosition();
		switch(entries[0].isIntersecting){
			case false:
			clearInterval(sid[i]);
			break;
			case true:
			if (y[i] === baseY[i] && Math.abs(v[i]) <= gravity / 2){
			clearInterval(sid[i]);//条件満たした際に止める
			}
		}
	})}
}else{
	for (let i = 0; i < itemElem.length; i++) {
		y[i]=y0[i];
		v[i]=0;
		itemElem[i].style.top =y[i]+size[i] + 'px';
		itemElem[i].style.left = x[i] + 'px';
		}
	}
};

Discussion