HTML5のcanvas要素に2次元の図形を描画します
図形の描画
スクリプトで描画コンテキストを取得します
*width属性とheight属性の初期値キャンバスは幅300ピクセル、高さ150ピクセル
<canvas style="border:solid 1px #ccc;"></canvas>
const canvas = document.querySelector("canvas");
const c = canvas.getContext("2d");
キャンバスのサイズをウィンドウのサイズにする
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
四角形の描画
fillRect(x, y, width, height)
塗りつぶされた四角形を描きますstrokeRect(x, y, width, height)
四角形の輪郭を描きます。clearRect(x, y, width, height)
指定された領域を消去し透明にします
const canvas = document.querySelector('canvas')
const c = canvas1.getContext("2d");
c.fillRect(10, 50, 50, 50)
c.strokeRect(110, 50, 50, 50)
c.fillRect(210, 50, 50, 50)
c.clearRect(220, 60, 30, 30)
パスを描くbeginPath()
:新しいパスを作成moveTo(x, y)
: 始点を配置lineTo(x, y)
: xと yで指定した位置に直線を描くclosePath()
:直線をパスに追加し現在のサブパスの開始地点につなぐ
パスを輪郭を描く(線を引く):stroke()
パスの内部を塗りつぶし:fill()
const canvas = document.querySelector('canvas')
const c = canvas1.getContext("2d");
// 塗りつぶした三角形
c.beginPath();
c.moveTo(25, 25);
c.lineTo(105, 25);
c.lineTo(25, 105);
c.fill();
// 輪郭の三角形
c.beginPath();
c.moveTo(125, 125);
c.lineTo(125, 45);
c.lineTo(45, 125);
c.closePath();
c.stroke();
円と円弧(円周上の二点によって区切られた円周の部分)arc(x, y, radius, startAngle, endAngle, true)
x, y
:中心点
radius :半径startAngle
:開始角度endAngle
:終了角度
true:defaultで時計回り(falseは時計と反対)startAngleが0
・endAngleがMath.PI * 2
で円になる
const canvas = document.querySelector('canvas')
const c = canvas.getContext("2d");
c.beginPath();
c.arc(100, 75, 50, 0, Math.PI * 2)
c.fill();
c.beginPath();
c.arc(220, 75, 50, 0, Math.PI * 2)
c.stroke();
角度はラジアンで計算されます
度からラジアンへの変換:radians = (Math.PI/180)*degrees
ベジェ曲線quadraticCurveTo(cp1x, cp1y, x, y)
x および y で指定した終端へ、 cp1x および cp1y で指定した制御点を使用して二次ベジェ曲線bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
x および y で指定した終端へ、(cp1x, cp1y) および (cp2x, cp2y) で指定した制御点を使用して三次ベジェ曲線
Path2DでSVG パスデータを使用してキャンバスのパスを初期化
const canvas = document.querySelector('canvas')
const c = canvas.getContext('2d');
let p = new Path2D('M10,30 A20,20,0,0,1,50,30 A20,20,0,0,1,90,30 Q90,60,50,90 Q10,60,10,30 Z');
c.stroke(p);
テキストの描画fillText(text, x, y [, maxWidth])
x,yで指定した位置にテキストを塗りつぶして描画(任意で最大描画幅を指定)strokeText(text, x, y [, maxWidth])
x,yで指定した位置にテキストの輪郭を描画(任意で最大描画幅を指定)
const canvas = document.querySelector('canvas')
const c = canvas.getContext("2d");
c.font = '48px serif';
c.fillText('Canvas', 10, 50);
c.strokeText('Canvas', 10, 125);
font = テキストのスタイル
既定のフォントは10px sans-seriftextAlign = start、end、left、right、center
を指定
既定値は starttextBaseline = value top、hanging、middle、alphabetic、ideographic、bottom
を指定
既定値は alphabeticdirection = ltr、rtl、inherit
(書字方向)
既定値は inherit
//canvasの中央
const canvas = document.querySelector('canvas')
const c = canvas15.getContext("2d");
c.font = '32px serif';
c.textAlign ='center';
c.textBaseline ='middle';
c.fillText('Canvas', canvas.width/2, canvas.height/2);
備考(便利な関数)
テキストは最大幅に収まるようにラップして表示する
<canvas style="width:100%"></canvas>
<script>
//最大幅に収まる長さのテキストを配列に格納する関数
function createTextArray(c, text, maxWidth) {
const textArray = []
const wordsArray = Array.from(text); //1文字ずつ格納配列にする
const results = wordsArray.reduce((accu, word) => {
// measureText()テキストの描画幅がmaxWidthより大きいとき
if (c.measureText(accu).width > maxWidth && accu.includes("")) {
//空白があれば空白で区切る
let words = accu.split(" ");
accu = words[words.length - 1]; //最後の空白以降の文字
words.pop();
if (words.join().replace(/,/g, " ") !== "") {
textArray.push(words.join().replace(/,/g, " "));
}
}
if (c.measureText(accu).width > maxWidth) {
textArray.push(accu); //textArrayに格納して
accu = ""; //空にする
}
return accu + word;
}, '');
textArray.push(results);
return textArray;
}
//テキストの配列を描画する関数
function drawTextArray(c, array, textX, textY, lineHeight) {
array.forEach((el, index) => {
c.fillText(
el,
textX,
textY -
(lineHeight * array.length) / 2 +
index * lineHeight +
lineHeight / 2
);
});
}
const canvas = document.querySelector('canvas')
const c = canvas.getContext("2d");
const w = canvas.clientWidth
canvas.width = w;
canvas.height = 200;
c.font = '32px serif';
c.textAlign ='center';
c.textBaseline ='middle';
const textArray = createTextArray(c, 'HAPPY BIRTHDAY TO YOU CONGRATS!!', canvas.width*0.7)
drawTextArray(c, textArray, canvas.width/2, canvas.height/2, 32*1.2)
</script>
画像を貼り付けるctx.drawImage(image, dx, dy)
image:Image, Canvas, Videoのいずれかの要素
dx, dy:位置を指定(canvasの左上が0)
const canvas = document.querySelector('canvas')
const c = canvas.getContext("2d");
const img = new Image();
img.src = "/image/...."; // 画像のURLを指定
img.onload = () => {
c.drawImage(img, 0, 0);
};
元画像を縮小・拡大してから貼り付けるctx.drawImage(image, dx, dy, dWidth, dHeight)
dWidth, dHeight:横幅、高さ
*アスペクト比が異なると歪む
img.onload = () => {
c.drawImage(img2, 0, 0, 300, 150);
};
img.addEventListener('load', () => {
c.drawImage(img2, 0, 0, 300, 150)
})
画像を切り抜くdrawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
image から左上の隅が (sx, sy)、幅と高さが sWidth および sHeight 領域を取得して
dx, dyで示した位置に配置してdWidthとdHeight で指定したサイズに拡大縮小
画像をbase64エンコードしたデータを読み込むこともできます
var img = new Image();
img.src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn....';
スタイルと色の適用
色と透明度
fillStyle = color
図形を塗りつぶす色strokeStyle = color
図形の輪郭の色globalAlpha = 透明度
const canvas = document.querySelector('canvas')
const c = canvas.getContext("2d");
const colors = [
'#B1B2FF',
'#AAC4FF',
'#D2DAFF',
'#EEF1FF',
'#F9F9F9'
]
for(let i = 0; i <20; i++){
let x = Math.random() * 300
let y = Math.random() * 150
c.beginPath();
c.arc(x, y, 30, 0, Math.PI * 2)
c.fillStyle = `hsl( ${ i * Math.floor(60/20) + 180}, 50% ,50%)`;
//colorsの配列からランダムに選ぶとき
// c.fillStyle = colors[Math.floor(Math.random()*colors.length)];
c.fill();
}
RGBとHSLについて
*同系色や明暗をランダムに作りたいときはH(色相)を使います
- RGBは赤・緑・青
黒:0,0,0
白:255,255,255
赤:255,0,0
緑:0,255,0
青:0,0,255 - HSLは色相(Hue)・彩度(Saturation)・輝度(Lightness)
Hue:360度の角度で指定
*0(360)は赤・90は緑・180は青・270は紫
Saturation:彩度(0%〜100%)で指定
*0は灰色 100は純色
Lightness:明度(0%〜100%)で指定
*0は黒 50は原色 100は白
線のスタイル
- lineWidth = 線の幅
- lineCap=線の端点
- butt:四角く切り落す
- round:丸める
- square:線の太さと同じ幅と半分の高さのボックスを追加して四角くする
- lineJoin=2つ区間の結合
- round:形状の角を丸くします(角の半径は線幅の半分と同じ)
- bevel:三角形の領域を追加して埋める
- miter:外側のエッジを延長し接続
save()
:canvasの描画情報を保存restore()
:canvasの描画情報を復元setTransform(伸縮x, 傾斜y, 傾斜x, 伸縮y, 移動x, 移動y)
:変形する
<canvas id="cv"></canvas>
<button type="button" onclick="restoreText()">復元</button>
<script>
const ctx = document.getElementById("cv").getContext("2d");
ctx.fillStyle ="pink";
ctx.strokeStyle = "black";
ctx.font = "32px Arial";
ctx.textBaseline = "top";
for ( let i = 0; i < 3 ; i++ ){
ctx.setTransform(1, 0, 0, 1, 0, 50*i);
ctx.fillRect(0,0, 300, 40);
ctx.save();
}
function restoreText(){
ctx.restore();
ctx.strokeText("復元", 40, 5);
}
</script>
アニメーション
requestAnimationFrameについて
requestAnimationFrame(callback)
引数に実行したい関数を渡すとその関数がブラウザにとって最適なタイミングで処理される
描画のタイミングに合わせて指定したコールバック関数を実行
*60FPS(FPSは1秒間の動画が何枚の画像で構成されているかを示すの単位)がデフォルト
60FPSの場合1000ms ÷ 60 = 16.6666… msに1回指定したコールバック関数が実行される
function draw() {
window.requestAnimationFrame(draw);
}
draw()
requestAnimationFrameを止める
*cancelAnimationFrame(id)
<div style="border:solid 1px #ccc;">
<h1 id="countup" style="text-align:center;">1</h1>
<button id="start-btn" type="button"">スタート</button>
</div>
<script>
const startBtn = document.querySelector('#start-btn')
let i = 0;
const loop = function() {
let id = window.requestAnimationFrame(loop);
const.textContent = i;
i++;
if (i === 100){
window.cancelAnimationFrame(id)
i = 0
};
}
loop();
startBtn.addEventListener("click", loop)
</script>
1
タイムスタンプ(呼び出された時点の時刻)を引数に受け取れます
*経過時間だけアニメーションしたい場合などにタイムスタンプを引数にします
経過時間(elapsed):最初のタイムスタンプの差分
duration:アニメーションの開始からの経過時間(ミリ秒)
0.1 * elapsed
ついでにw1秒間に何回コールバック関数が呼び出されたかを表示
1秒間(1000 ミリ秒)動作の例
<p>経過時間 : <span id="elapsed"></span></p>
<p>duration : <span id="shaping"></p>
<p>コールバック回数 : <span id="fps"></p><
<button type="button" id="play">スタート</button>
<script>
let start;
let fps, prevTimestamap; //FPS用
let frameCount = 0;//FPS用
const e_val = document.getElementById('elapsed');
const s_val = document.getElementById('shaping');
const f_val = document.getElementById('fps');
let clearId
function count (timestamp){
if (start === undefined) {
start = timestamp;
}
let fps = prevTimestamap ? Math.floor((timestamp - prevTimestamap)*10000)/10000 : 0;
prevTimestamap = timestamp; //FPS用
frameCount++; //FPS用
let elapsed = start ? timestamp - start : 0;
let x = Math.min(0.1 * elapsed, 100);
s_val.textContent = x;
e_val.textContent = elapsed;
f_val.textContent = fps
//コールバック回数の計算用
if(elapsed > 1000) {
fps = frameCount;
frameCount = 0;
start = timestamp;
}
//1秒間だけ 経過時間とduration
if (elapsed < 1000) {
clearID = window.requestAnimationFrame(count);
} else{
window.cancelAnimationFrame(clearID)
}
}
const play = document.getElementById('play');
play.addEventListener('click', (e)=> {
start = undefined;
frameCount = 0;
count();
});
</script>
経過時間 :
duration :
コールバック回数 :
Canvasに描画した図形のアニメーション
アニメーションさせる場合にも移動する部分と以前に描いた部分をすべて再描画する必要があります
横(x軸)を移動するclearRect()
で以前に描画した図形をすべてクリアする必要があります
const canvas = document.querySelector('canvas')
const c = canvas.getContext("2d");
let x = 50
let dx = 2 // 速度(大きくすると速度が速くなる)
let radius = 30 //半径
function move() {
const id= window.requestAnimationFrame(move);
c.clearRect(0, 0, 300, 150);
c.beginPath();
c.arc(x, 75, 30, 0, Math.PI * 2);
c.fillStyle = 'blue';
c.fill();
x += dx
if(x+radius > 300 || x-radius < 0){
dx = -dx
}
if (x-radius < 0) window.cancelAnimationFrame(id);
}
円をクラスにしてxとy方向への動きをつける
<canvas style="width:100%;"></canvas>
<div style="display:flex;">
<button id="start-btn" type="button">スタート</button>
<button id="stop-btn" type="button">ストップ</button>
</div>
const startBtn = document.querySelector('#start-btn')
const stopBtn = document.querySelector('#stop-btn')
const canvas = document.querySelector('canvas')
//幅を取得
const w = canvas.clientWidth
canvas.width=w;
canvas.height=200; //高さ200px
const c = canvas.getContext("2d");
//円を作成
class Circle{
constructor(x, y, dx, dy, radius){
this.x=x;
this.y=y;
this.dx = dx;
this.dy = dy;
this.radius= radius;
}
draw(){
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
c.fillStyle = 'blue';
c.fill();
}
move(){
if(this.x+this.radius > canvas.width || this.x-this.radius < 0){
this.dx = -this.dx
}
if(this.y+this.radius > canvas.height || this.y-this.radius < 0){
this.dy = -this.dy
}
this.x += this.dx;
this.y += this.dy;
this.draw()
}
}
//円の初期化
const circle = new Circle(20, 20, 3, 3,10)
let id;
//アニメーションする
function animation(){
id= window.requestAnimationFrame(animation);
c.clearRect(0, 0, canvas.width, canvas.height);
circle.move();
}
//スタート
startBtn.addEventListener("click", animation)
//ストップ
stopBtn.addEventListener("click", function(e) {
window.cancelAnimationFrame(id);
});
円の色をランダムに変更
複数の円インスタンスを描画する
const colors = [
'#B1B2FF',
'#AAC4FF',
'#D2DAFF',
'#EEF1FF',
'#F9F9F9'
]
class Circle{
constructor(x, y, dx, dy, radius){
//省略
this.color = colors[Math.floor(Math.random()*colors.length)];
}
draw(){
//省略
c.strokeStyle = this.color;
c.stroke();
}
move(){
//省略
}
}
//インスタンスを格納する配列
const circleArray =[];
for(let i = 0; i < 100; i++){
let radius = 20
let x = Math.random() * (w - radius*2) + radius
let y = Math.random() * (200 - radius*2)+ radius
let dx = Math.random()
let dy = Math.random()
circleArray.push(new Circle2(x, y, dx, dy, radius))
}
function animation(){
id = window.requestAnimationFrame(animation);
c.clearRect(0, 0, canvas.width, canvas.height);
circleArray.forEach(circle=>{
circle.move();
})
}
リサイズ時に対応する
let circleArray =[];
//図形オブジェクトを格納(初期化)
function init(){
circleArray =[]
for(let i = 0; i < 100; i++){
let radius = 20
let x = Math.random() * (w - radius*2) + radius
let y = Math.random() * (200 - radius*2)+ radius
let dx = Math.random()
let dy = Math.random()
circleArray.push(new Circle2(x, y, dx, dy, radius))
}
}
init()
function animation(){
id = window.requestAnimationFrame(animation);
c.clearRect(0, 0, w, 200);
circleArray.forEach(circle=>{
circle.move();
})
}
//リサイズ時
window.addEventListener('resize', function(){
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
init() //図形オブジェクト格納を初期化する
})
縦(Y軸)方向に重力を表現する
move(){
if(this.y+this.radius + this.dy > canvas.height){
this.dy = -this.dy * 0.95
} else{
this.dy += 1
}
this.y += this.dy;
}
便利な関数
minとmaxのあいだでランダムな数を生成
*注意「0 〜 10」であれば「11」が最大値
function randomRange(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min)
}
コードを見る
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>デモページ</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #3e4444;
}
</style>
</head>
<body>
<canvas></canvas>
<script>
const colors = [
'#B1B2FF',
'#AAC4FF',
'#D2DAFF',
'#EEF1FF',
'#F9F9F9'
]
const canvas = document.querySelector('canvas')
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const c = canvas.getContext("2d");
function randomRange(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min)
}
class Circle{
constructor(x, y, dx, dy, radius){
this.x=x;
this.y=y;
this.dx = dx;
this.dy = dy;
this.radius= radius;
this.color = colors[Math.floor(Math.random()*colors.length)];
}
draw(){
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
c.fillStyle = this.color;;
c.fill();
}
move(){
if(this.x + this.radius > canvas.width || this.x - this.radius < 0){
this.dx = -this.dx
}
if(this.y + this.radius + this.dy > canvas.height){
this.dy = -this.dy * 0.95
} else{
this.dy += 1
}
this.x += this.dx;
this.y += this.dy;
this.draw()
}
}
let circleArray =[];
function init(){
circleArray =[];
for(let i = 0; i < 200; i++){
let radius = randomRange(3, 30)
let x = Math.random() * (canvas.width - radius*2) + radius
let y = Math.random() * (canvas.height - radius*2)+ radius
let dx = Math.random()*0.5
let dy = Math.random()*0.5
circleArray.push(new Circle(x, y, dx, dy, radius))
}
}
init();
function animation(){
window.requestAnimationFrame(animation);
c.clearRect(0, 0, canvas.width, canvas.height);
circleArray.forEach(circle=>{
circle.move();
})
}
animation()
window.addEventListener('resize', function(){
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
init()
})
</script>
</body>
</html>
マウスイベント
マウスイベント備忘録
- mousedown :ボタンが押されたとき
- mouseup :ボタンが離されたとき
- mouseover :ポインタが要素に乗ったとき
- mouseout: ポインタが要素から離れたとき
- mouseenter: ポインタが要素に乗ったとき( バブリングなし)
- mouseleave :ポインタが要素から離れたとき (バブリングなし)
- mousemove :ポインタが要素の上を移動したとき
- offsetX / Y
要素内での座標(マウスが載っている要素の左上を原点とした座標)を取得 - clientX / Y
ウィンドウ内でのカーソル座標を取得(ウィンドウの左上が (0, 0))
マウスでお絵描き
<canvas id="myCanvas" style="border:solid 1px #ccc;"></canvas>
<script>
let isDrawing = false;
let x = 0;
let y = 0;
const myCanvas = document.getElementById('myCanvas');
const c = myCanvas.getContext('2d');
function drawLine(c, x1, y1, x2, y2) {
c.beginPath();
c.strokeStyle = 'black';
c.lineWidth = 1;
c.moveTo(x1, y1);
c.lineTo(x2, y2);
c.stroke();
c.closePath();
}
myCanvas.addEventListener('mousedown', e => {
x = e.offsetX;
y = e.offsetY;
isDrawing = true;
});
myCanvas.addEventListener('mousemove', e => {
if (isDrawing === true) {
drawLine(c, x, y, e.offsetX, e.offsetY);
x = e.offsetX;
y = e.offsetY;
}
});
window.addEventListener('mouseup', e => {
if (isDrawing === true) {
drawLine(c, x, y, e.offsetX, e.offsetY);
x = 0;
y = 0;
isDrawing = false;
}
});
</script>
円と円の距離
備考(便利な関数)
ピタゴラスの定理を利用して2つの円の距離(円の中心からの距離)を計算する
function distance(x1, y1, x2, y2) {
const xDist = x2 - x1
const yDist = y2 - y1
return Math.sqrt(Math.pow(xDist, 2) + Math.pow(yDist, 2))
}
マウスに追随する円が円弧に接触したら緑色に変化する
*2つの円の距離が2つの円の半径を足した長さより短くなれば接触している
const canvas = document.querySelector('canvas')
const c = canvas.getContext("2d");
function distance(x1, y1, x2, y2) {
const xDist = x2 - x1
const yDist = y2 - y1
return Math.sqrt(Math.pow(xDist, 2) + Math.pow(yDist, 2))
}
const radius1 = 10;
const radius2 = 30;
const mouse = {
x:10,
y:10
}
c.beginPath();
c.arc(220, 75, radius2, 0, Math.PI * 2)
c.stroke();
canvas.addEventListener('mousemove', (e) => {
mouse.x = e.offsetX
mouse.y = e.offsetY
c.clearRect(0, 0, 300, 150)
c.beginPath();
c.arc(mouse.x, mouse.y, radius1, 0, Math.PI * 2)
c.fill();
c.fillStyle='red';
c.beginPath();
c.arc(220, 75, radius2, 0, Math.PI * 2)
c.stroke();
if(distance(mouse.x, mouse.y, 220, 75) < radius1+radius2){
c.fillStyle='green';
}
})
衝突した時に角度を変更して回避するための関数(便利な関数)
https://www.youtube.com/watchv=789weryntzM&list=PLpPnRKq7eNW3We9VdCfx9fprhqXHwTPXL&index=7(ありがとうございます💓)
function rotate(velocity, angle) {
const rotatedVelocities = {
x: velocity.x * Math.cos(angle) - velocity.y * Math.sin(angle),
y: velocity.x * Math.sin(angle) + velocity.y * Math.cos(angle)
};
return rotatedVelocities;
}
function resolveCollision(particle, otherParticle) {
const xVelocityDiff = particle.velocity.x - otherParticle.velocity.x;
const yVelocityDiff = particle.velocity.y - otherParticle.velocity.y;
const xDist = otherParticle.x - particle.x;
const yDist = otherParticle.y - particle.y;
if (xVelocityDiff * xDist + yVelocityDiff * yDist >= 0) {
const angle = - Math.atan2(otherParticle.y - particle.y, otherParticle.x - particle.x);
const m1 = particle.mass;
const m2 = otherParticle.mass;
const u1 = rotate(particle.velocity, angle);
const u2 = rotate(otherParticle.velocity, angle);
const v1 = { x: u1.x * (m1 - m2) / (m1 + m2) + u2.x * 2 * m2 / (m1 + m2), y: u1.y };
const v2 = { x: u2.x * (m1 - m2) / (m1 + m2) + u1.x * 2 * m2 / (m1 + m2), y: u2.y };
const vFinal1 = rotate(v1, -angle);
const vFinal2 = rotate(v2, -angle);
particle.velocity.x = vFinal1.x;
particle.velocity.y = vFinal1.y;
otherParticle.velocity.x = vFinal2.x;
otherParticle.velocity.y = vFinal2.y;
}
}
コードを見る
<div id="canvas-box" style="width:100%;">
<canvas></canvas>
</div>
const canvas = document.querySelector('canvas')
const w = document.querySelector('#canvas-box').clientWidth
canvas.width = w;
canvas.height = 200;
const c = canvas.getContext("2d");
const colors = [
'#B1B2FF',
'#AAC4FF',
'#D2DAFF',
'#EEF1FF'
]
//minとmaxの間でランダムな数を生成
function randomRange(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min)
}
class Partcles{
constructor(x, y, radius){
this.x = x;
this.y = y;
this.mass = 1; //resolveCollision関数で必要
this.velocity = {
x: Math.random(),
y: Math.random()
}; //resolveCollision関数ように(dx dy)はvelocityオブジェクトに
this.radius= radius;
this.color = colors[Math.floor(Math.random()*colors.length)];
}
draw(){
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
c.fillStyle = this.color;;
c.fill();
}
update(lists){
this.draw()
//衝突した時の修正
for(let i = 0; i < lists.length; i++){
if(this===lists[i]) continue;
if (distance(this.x, this.y, lists[i].x, lists[i].y) - this.radius*2 < 0){
resolveCollision(this, lists[i])
}
}
//横枠の範囲
if(this.x + this.radius > canvas.width || this.x - this.radius < 0){
this.velocity.x = -this.velocity.x
}
//縦枠の範囲
if(this.y + this.radius > canvas.height || this.y - this.radius < 0){
this.velocity.y = -this.velocity.y
}
//動き
this.x += this.velocity.x;
this.y += this.velocity.y;
}
}
let partcles =[];
function init(){
partcles =[];
for(let i = 0; i < 10; i++){
let radius = 30
let x = randomRange(radius, canvas.width - radius )
let y = randomRange(radius, canvas.height - radius)
//円オブジェクト生成時のxとyが衝突しないようにする
if(i !== 0){
for(let j = 0; j < partcles.length; j++){
if(distance(x, y, partcles[j].x, partcles[j].y) - radius*2 < 0){
x = randomRange(radius, canvas.width - radius )
y = randomRange(radius, canvas.height - radius)
j = -1;
}
}
}
partcles.push(new Partcles(x, y, radius))
}
}
function animation(){
window.requestAnimationFrame(animation);
c.clearRect(0, 0, canvas.width, canvas.height);
partcles.forEach(partcle=>{
partcle.update(partcles);
})
}
init();
animation();
//画面リサイズ時の対応
window.addEventListener('resize', function(){
canvas.width=w;
canvas.height=200;
init()
})