打ち出したボールが何かに衝突して跳ね返るみたいなコードを探す。
究極かもしれないと思えるサンプルコードを発見!
endo blog: HTML5 – Canvas 円同士の衝突アニメーション
しかし例のごとく setInterval で clearInterval していない。
無限に増殖する円が延々衝突し続けるという究極の CPU 殺しでもあった。
一生懸命自己解析していたら CPU ファンが凄い勢いで回り初めて焦った。
もしファンの無いスマホなんかで見ていたら…
しかたがないので停止や初期化のボタンを付けてコピペ。
TimerClass をインスタンス化したら start(func), stop() メソッドで簡単に使えるようにしてみた。
それをスマートフォン用にレイアウト、のつもり。
肝心のアルゴリズムはコピペです、すんません。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>球の衝突(負荷強)</title>
<!-- for Smart Phone -->
<meta name="viewport" content="
width=device-width,
initial-scale=1.0,
minimum-scale=1.0,
maximum-scale=1.0,
user-scalable=no" />
<style>
body {
margin: 0;
overflow: hidden;
-webkit-text-size-adjust: 100%;
}
img {
margin: 0;
padding: 0;
overflow: hidden;
}
#ID_CANVAS {
background-color: #FFFFFF;
}
</style>
<script type="text/javascript"><!--
var canvas = null;
var context = null;
var timer = null;
var cw = 0;
var ch = 0;
var g = 0.1;
const INTERVAL = 10;
var circles = [];
var colorList = ['0','1','2','3','4','5','6','7',
'8','9','a','b','c','d','e','f'];
var TimerClass = function() {
this.count = 0;
this.id = -1;
this.start = function(func) {
if (this.id == -1) {
this.id = setInterval(func, INTERVAL);
}
}
this.stop = function() {
if (this.id > -1) {
clearInterval(this.id);
this.id = -1;
}
}
}
var onTimer = function() {
context.clearRect(0, 0, cw, ch);
if (timer.count>100 || timer.count==0) {
timer.count = 0;
circles.push(new Circle());
circles[circles.length-1].init(circles.length % 2);
}
for (var i=0; i<circles.length; i++) {
for(var j=i+1; j<circles.length; j++) {
collisionCircleCircle(circles[i], circles[j]);
}
}
for (var i=0; i<circles.length; i++) {
circles[i].move();
circles[i].collisionWall();
circles[i].view();
}
timer.count++;
}
function Circle(){
this.h = 0.9;
this.x = 0;
this.y = 100;
this.vx = 0;
this.vy = 0;
this.r = 0;
this.color = '#';
this.init = function(vec) {
this.vx = Math.random() * 9 + 1;
this.r = Math.random() * 25 + 5;
if (vec) {
this.x = cw + 100;
this.vx *= -1;
} else {
this.x = -100;
}
this.color += colorList[Math.floor(Math.random() * 3) + 3];
this.color += colorList[Math.floor(Math.random() * 5) + 5];
this.color += colorList[Math.floor(Math.random() * 7) + 9];
}
this.move = function() {
this.x += this.vx;
this.y += this.vy;
this.vy += g;
}
this.view = function() {
context.beginPath();
context.fillStyle = this.color;
context.arc(this.x, this.y, this.r, 0, 360, false);
context.fill();
}
this.collisionWall = function() {
if (this.x+this.r > cw && this.vx > 0 || this.x-this.r < 0 && this.vx < 0) {
this.vx *= -this.h;
} else if (this.y+this.r > ch && this.vy > 0 || this.y-this.r < 0 && this.vy < 0) {
this.vy *= -this.h;
}
}
}
function collisionCircleCircle(a,b){
if( (b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y) < (a.r+b.r)*(a.r+b.r) ){
var vx = a.x-b.x;
var vy = a.y-b.y;
var len = Math.sqrt(vx*vx+vy*vy);
var d = a.r+b.r-len;
if (len>0)
len =1/len;
vx *= len;
vy *= len;
d /= 2.0;
a.x += vx*d;
a.y += vy*d;
b.x -= vx*d;
b.y -= vy*d;
var t;
t = -(vx*a.vx+vy*a.vy)/(vx*vx+vy*vy);
var arx = a.vx+vx*t;
var ary = a.vy+vy*t;
t = -(-vy*a.vx+vx*a.vy)/(vy*vy+vx*vx);
var amx = a.vx-vy*t;
var amy = a.vy+vx*t;
t = -(vx*b.vx+vy*b.vy)/(vx*vx+vy*vy);
var brx = b.vx+vx*t;
var bry = b.vy+vy*t;
t = -(-vy*b.vx+vx*b.vy)/(vy*vy+vx*vx);
var bmx = b.vx-vy*t;
var bmy = b.vy+vx*t;
var e = 0.8;
var adx = (a.r*amx+b.r*bmx+bmx*e*b.r-amx*e*b.r)/(a.r+b.r);
var bdx = -e*(bmx-amx)+adx;
var ady = (a.r*amy+b.r*bmy+bmy*e*b.r-amy*e*b.r)/(a.r+b.r);
var bdy = -e*(bmy-amy)+ady;
a.vx = adx+arx;
a.vy = ady+ary;
b.vx = bdx+brx;
b.vy = bdy+bry;
}
}
var init = function() {
if (window.TouchEvent) {
cw = window.innerWidth;
ch = window.innerHeight - 60;
timer = new TimerClass();
// Create Canvas and Context
canvas = document.getElementById("ID_CANVAS");
canvas.width = cw;
canvas.height = ch;
context = canvas.getContext("2d");
// Create ToolBar
var back = document.getElementById("ID_BACK");
back.style.top = ch + "px";
back.style.left = 0 + "px";
back.addEventListener("touchend", function() {
document.location = ".";
});
var start = document.getElementById("ID_START");
start.style.top = ch + "px";
start.style.left = 80 + "px";
start.addEventListener("touchend", function() {
timer.start(onTimer);
});
var stop = document.getElementById("ID_STOP");
stop.style.top = ch + "px";
stop.style.left = 160 + "px";
stop.addEventListener("touchend", function() {
timer.stop();
});
var clear = document.getElementById("ID_CLEAR");
clear.style.top = ch + "px";
clear.style.left = 240 + "px";
clear.addEventListener("touchend", function() {
timer.stop();
circles = [];
context.clearRect(0, 0, cw, ch);
});
}
}
//-->
</script>
</head>
<body onLoad="init()">
<canvas id="ID_CANVAS"></canvas>
<img id="ID_BACK" src="button1/back.png" style="position:absolute">
<img id="ID_START" src="button1/start.png" style="position:absolute">
<img id="ID_STOP" src="button1/stop.png" style="position:absolute">
<img id="ID_CLEAR" src="button1/clear.png" style="position:absolute">
</body>
</html>
はっきりいって何がどうなってこうなるかよくワカンネェ!
重力で落下するみたいな処理も多分入っているよな、どこだか解らないけど。
何日かチマチマ弄っていればそのうち少しは理解できる、といいな。
今回は覚書とタイマーの簡単な利用方法、ということで。
それにしても神様、私にもっと絵心というものを下さい。
ボタンが我ながらダサすぎる、今後の課題はイラストかも。
