sinとcos
波線を描く
const canvas = document.querySelector('canvas')
const c = canvas.getContext("2d");
c.beginPath();
c.moveTo(0, canvas.height/2)
//1pxごとにlineTo
for(let i = 0; i < canvas.width; i++){
c.lineTo(i, canvas.height/2 + Math.sin(i * 0.05) * 50)
}
c.stroke()
*Math.sin(radians)
を使って波を動かし、色を変化させる(「-1」~「1」の間の数)
*clearRectのかわりにfillRectでcanvasをクリア(半透明に)する
const w = document.querySelector('#canvas-box').clientWidth
const canvas = document.querySelector('canvas')
const c = canvas.getContext("2d");
canvas.width = w;
canvas.height = 200;
const wave ={
y:canvas.height/2,
amplitude : 75, //振幅
length: 0.02, //幅
frequency:0.01 //周波数
}
let inc = wave.frequency;
function animation(){
window.requestAnimationFrame(animation);
//背景は透明度のある黒
c.fillStyle ='rgba(0,0,0,0.01)'
c.fillRect(0, 0, canvas.width, canvas.height);
c.beginPath();
c.moveTo(0, wave.y)
for(let i = 0; i < canvas.width; i++){
c.lineTo(i, wave.y + Math.sin(i * wave.length + inc ) * wave.amplitude * Math.sin(inc))
}
//Math.abs()で絶対値を取得(マイナス時の対策)
c.strokeStyle=`hsl(${ Math.abs(Math.sin(inc)* 360)}, 50%, 50%)`
c.stroke()
inc += wave.frequency //加算する
}
animation()
Math.cos(radians)とMath.sin(radians)
x座標:cos(0) =1
cos(90) = 0
y座標:sin(0) = 0
sin(90) = 1
(1,0)は(cos(0), sin(0))
(0, 1)は(cos(90), sin(90))
<canvas width="300" height="300"></canvas>
const canvas = document.querySelector('canvas')
const c = canvas.getContext("2d");
let x = canvas.width/2
let y = 50
let radius = 10
//速度(数字が小さいと円が大きくなる)
let velocity = 0.05
//角度ラジアン
let radians = 0
function move() {
window.requestAnimationFrame(move);
c.clearRect(0, 0, 300, 300);
c.beginPath();
c.arc(x, y, radius, 0, Math.PI * 2);
c.fillStyle = 'blue';
c.fill();
radians += velocity;
x = x + Math.cos(radians)* 5;
y = y + Math.sin(radians)* 5;
}
move()
複数の円をMath.cos(radians)
とMath.sin(radians)
を利用して動かす
const canvas = document.querySelector('canvas')
const c = canvas.getContext("2d");
class Particle{
constructor(x, y, radius, color, velocity){
this.x = x;
this.y = y;
this.radius= radius;
this.color = color;
this.velocity = velocity;
}
draw(){
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
c.fillStyle = this.color;
c.fill();
c.closePath()
}
update(){
this.draw()
this.x += this.velocity.x
this.y += this.velocity.y
if(this.x > canvas.width || this.x < 0){
this.velocity.x = -this.velocity.x
}
if(this.y > canvas.height || this.y < 0){
this.velocity.y = -this.velocity.y
}
}
}
const particleCount = 30
// 360度を円の数で割る
const angleInc = Math.PI * 2 / particleCount
let particles=[];
function init(){
for(let i = 0; i < particleCount; i++){
particles.push(new Particle(150, 150, 10, `hsl(${360/particleCount * i}, 100%, 50%)`,
{ x : Math.cos(angleInc * i), y : Math.sin(angleInc * i)}
))
}
}
function animation(){
window.requestAnimationFrame(animation);
c.clearRect(0, 0, canvas.width, canvas.height);
particles.forEach( particle=>{
particle.update();
})
}
init()
animation()
応用
花火を作る(ロード時とクリック時に表示される)
ランダムな粒子にして、大きく広がり最後に粒子を消す
コードを見る
<!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: black;
}
canvas{
position: absolute;
top:0;
left: 0;
}
</style>
</head>
<body>
<canvas></canvas>
<script>
const canvas = document.querySelector('canvas')
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const c = canvas.getContext("2d");
const gravity = 0.005 //重力
const power = 0.95 //広がり
class Particle{
//x,y中心点 ・粒子の半径・色・velocityはオブジェクト {x,y}
constructor(x, y, radius, color, velocity){
this.x=x;
this.y=y;
this.radius= radius;
this.color = color;
this.velocity = velocity;
this.alpha = 1 //
}
draw(){
c.save() //陰影を消すため
c.globalAlpha = this.alpha
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
c.fillStyle = this.color;;
c.fill();
c.restore()//陰影を消すため
}
update(){
this.draw()
this.velocity.x *= power //x方向の広がり
this.velocity.y *= power //y方向の広がり
this.velocity.y += gravity//y方向重力
this.x += this.velocity.x
this.y += this.velocity.y
this.alpha -= 0.005 //粒子を消す為
}
}
let particles;
function init(x,y){
particles =[];
const particleCount = 1000
const angleInc = Math.PI * 2 / particleCount
const power = 40
let px = x;
let py = y;
for(let i = 0; i < particleCount; i++){
particles.push(new Particle(px, py, 3, `hsl(${Math.random()*360}, 100%, 50%)`,
{
x:Math.cos(angleInc * i) * Math.random()*power,
y:Math.sin(angleInc * i) * Math.random()*power
}
))
}
}
function animation(){
window.requestAnimationFrame(animation);
c.fillStyle ='rgba(0,0,0,0.05)'
c.fillRect(0, 0, canvas.width, canvas.height);
particles.forEach( (particle, index)=>{
if(particle.alpha > 0){
particle.update();
}else{
particles.splice(index, 1) //粒子を消す
}
})
}
//ロード時は中心
init(canvas.width/2, canvas.height/2);
window.addEventListener('load', animation)
//クリック時
canvas.addEventListener('click', (e)=>{
init(e.clientX, e.clientY,)
})
//リサイズ時
window.addEventListener('resize', ()=>{
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
init(canvas.width/2, canvas.height/2);
})
</script>
</body>
</html>
CSSのfilterを利用する
スライムの動きはCSSでfilterを利用します
*canvasがblurでぼやけるので親要素にはbackground-colorとoverflow:hidden
<div style="background:black; width:300px; height:300px; overflow:hidden;" >
<canvas width="300" height="300"></canvas>
</div>
<style>
canvas{
background:black;
filter:blur(10px) contrast(20);
}
</style>
<script>
class Setting{
constructor(width, height){
this.instanceArray=[]
this.width = width
this.height = height
}
init(number){
for(let i = 0; i < number; i++){
this.instanceArray.push(new Ball(this))
}
}
render(c){
this.instanceArray.forEach(ball=>ball.update(c))
}
}
class Ball{
constructor(setting){
this.setting = setting
this.radius = Math.random()* 30 + 3
this.x = this.x = this.radius *2 +(Math.random()*(this.setting.width -this.radius *4))
this.y = this.radius*-1
this.speedX = Math.random()*0.2-0.1
this.speedY = Math.random()*0.5+0.2
this.g = Math.random()*0.005
this.vy =0
}
update(c){
if(this.x < this.radius || this.x > this.setting.width - this.radius) this.speedX *= -1
if( this.y > this.setting.height - this.radius){
this.radius = Math.random()* 30 + 3
this.x = this.radius *2 +(Math.random()*(this.setting.width -this.radius *4))
this.y = this.radius*-1
this.vy=0
this.speedY = Math.random()*0.5+0.2
}
if(this.y > this.radius*2){
this.vy += this.g
this.speedY +=this.vy
}
this.x += this.speedX
this.y += this.speedY
this.draw(c)
}
draw(c){
c.beginPath()
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2)
c.fill()
}
}
const canvas = document.querySelector('canvas')
const c = canvas1.getContext("2d")
const setting = new Setting(canvas.width, canvas.height)
setting.init(15)
function animation(){
c.clearRect(0, 0, canvas.width, canvas.height);
setting.render(c)
requestAnimationFrame(animation);
}
animation()
</script>
フィルターあり
フィルターなし
基本のテンプレート
初期化とレンダリング用のSettingクラスを作る
図形クラスのコンストラクタにSettingを渡す
class Setting{
constructor(width, height){
this.instanceArray=[]
this.width = width
this.height = height
}
init(number){
for(let i = 0; i < number; i++){
this.instanceArray.push(new Objects(this))
}
}
draw(c){
this.instanceArray.forEach(object=>object.draw(c))
}
render(c){
this.instanceArray.forEach(object=>object.update(c))
}
}
class Objects{
constructor(setting){
this.setting = setting
this.radius = 50
this.x = 100
this.y = 100
this.speedX = 1
this.speedY = 1
}
update(c){
this.draw(c)//今回はupdateでdrawメソッドを呼び出している
this.x += this.speedX
this.y += this.speedY
}
draw(c){
c.beginPath()
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2)
c.fill()
}
}
const canvas = document.querySelector('canvas')
const c = canvas.getContext("2d")
canvas.width = window.innerWidth
canvas.height = window.innerHeight
const setting = new Setting(canvas.width, canvas.height)
function animation(){
c.clearRect(0, 0, canvas.width, canvas.height)
setting.render(c)
requestAnimationFrame(animation)
}
setting.init(1)
animation()
//リサイズ
window.addEventListener("resize", () => {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
setting.init(1)
});
Canvasを回転させる
updateメソッドは不要
Canvasを回転させる
*粒子の移動はしないのでupdateメソッドは不要
save()で記録した時点をrestore()で復元する
rotate(radians)は左上が軸になるので
まずtranslate(canvas.width/2, canvas.height/2)で中心に
*描画範囲もズレるので、最初のxとyを四等分した左上の境界が最大値になるようにする
*画面の縦横比に対応するにはCanvasを大きめで星の数も多めに見積もる
const colors = [
'white',
'blue',
'yellow',
'orange',
]
class Setting{
constructor(width, height){
this.instanceArray=[]
this.width = width
this.height = height
}
init(number){
for(let i = 0; i < number; i++){
//四等分した左上の境界が最大値になる
const x = (Math.random() * (this.width+100)) - (this.width+100)/2
const y =( Math.random() * (this.height+100)) - (this.width+100) /2
const radius = Math.random() * 1
const color = colors[Math.floor(Math.random()*colors.length)]
this.instanceArray.push(new Objects(this, x, y, radius, color))
}
}
//今回はdrawだけでいい
draw(c){
this.instanceArray.forEach(object=>object.draw(c))
}
}
class Objects{
constructor(setting, x, y, radius, color){
this.setting = setting
this.radius = radius
this.x = x
this.y = y
this.color = color
}
//update不要
draw(c){
c.beginPath()
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2)
c.shadowColor = this.color //線や図形に影を付ける
c.shadowBlur = 50
c.fillStyle=this.color
c.fill()
}
}
const canvas = document.querySelector('canvas')
const c = canvas.getContext("2d")
const setting = new Setting(canvas.width, canvas.height)
let radians = 0
//Canvasを回転させる
function animation(){
c.fillStyle='rgba(10,10,10,1)' //rgba(10,10,10,0.03)陰影が残る
c.fillRect(0, 0, canvas.width, canvas.height)
c.save()
c.translate(canvas.width/2, canvas.height/2)
c.rotate(radians)
setting.draw(c)
c.restore()
radians += 0.005
requestAnimationFrame(animation)
}
setting.init(400)
animation()
rgba(10,10,10,1)
rgba(10,10,10,0.03)