yoppa.org


Blog

canvasのテスト

HTML5の目玉機能(?)、canvas要素とJavascriptでいろいろサンプル作成中。画面をマウスクリックでパーティクルが吸着するよ。

追記:ちょっと修正 (19:30)

Javascriptのソースはこんな感じ。

const NUM = 500; //円の数
const WIDTH = 400; //画面の幅
const HEIGHT = 400; //画面の高さ
var speedX = new Array(NUM); //X方向のスピードの配列
var speedY = new Array(NUM); //Y方向のスピードの配列
var locX = new Array(NUM); //円の中心位置のX座標の配列
var locY = new Array(NUM); //円の中心位置のY座標の配列
var radius = new Array(NUM); //円の半径の配列
var r =  new Array(NUM); //Red成分の配列
var g =  new Array(NUM); //Green成分の配列
var b =  new Array(NUM); //Blue成分の配列
var ctx; //描画コンテキスト
var mouseX, mouseY; //マウス座標
var mouseMoved = false; //マウスが移動しているか否か
var mouseDown = false;  //マウスクリックしているか否か

//ばねのパラメーター
var stiffness = 0.03, damping = 0.99, mass = 10.0;

window.onload = function() {
  setup();
};

function setup(){
  var canvas = document.getElementById('tutorial');
  if (canvas.getContext){
    ctx = canvas.getContext('2d');
    
    //イベントリスナー:マウス移動
    canvas.onmousemove = mouseMoveListner;
    canvas.onmousedown = mouseDownListner;
    canvas.onmouseup = mouseUpListner;
    
    //初期値を生成して配列に格納
    for(var i = 0; i < NUM; i++){
      locX[i] =  WIDTH / 2;
      locY[i] = HEIGHT / 2;
      radius[i] = Math.random() * 6.0 + 2.0;
      r[i] = Math.floor(Math.random() * 32);
      g[i] = Math.floor(Math.random() * 64);
      b[i] = Math.floor(Math.random() * 128);
    }
    //スピード初期化
    randomVector();
    //60fps
    setInterval(update, 1000.0/60.0);
  }
}

function update(){
  //もしマウスクリックしていたらばねの動きでマウスに吸着
  if(mouseDown){ 
    for(var i = 0; i < NUM; i++){
	    var forceX = stiffness * (mouseX - locX[i]);
	    var accelerationX = forceX / mass;
	    speedX[i] = damping * (speedX[i] + accelerationX);
	    var forceY = stiffness * (mouseY - locY[i]);
	    var accelerationY = forceY / mass;
	    speedY[i] = damping * (speedY[i] + accelerationY);
    }
  }
  //座標を更新
  for(var i = 0; i < NUM; i++){
    locX[i] += speedX[i];
    locY[i] += speedY[i];
    if(getLength(locX[i], locY[i], WIDTH/2 , HEIGHT/2) > WIDTH*0.75){
	    speedX[i] *= -1;
	    speedY[i] *= -1;
    }
  }
  //drawを実行
  draw();
}

function draw(){
  //背景を描画
  ctx.globalCompositeOperation = "source-over";
  ctx.fillStyle = "rgba(0,0,0,.1)";
  ctx.fillRect(0, 0, WIDTH, HEIGHT);
  //更新した座標で円を描く
  ctx.globalCompositeOperation = "lighter";
  for(var i = 0; i < NUM; i++){
    ctx.beginPath();
    ctx.fillStyle = 'rgba(' + r[i] + ',' + g[i] + ',' + b[i] + ', 0.8)';
    ctx.arc(locX[i], locY[i], radius[i], 0, Math.PI*2.0, true);
    ctx.fill();
  }
}

function mouseMoveListner(e) {
  //画面左上からのマウス座標取得
  adjustXY(e);
  //マウスを移動中に
  mouseMoved = true;
}

function mouseDownListner(e) {
  mouseDown = true;
}

function mouseUpListner(e) {
  mouseDown = false;
  randomVector();
}

//ランダムな方向にスピード設定
function randomVector(){
  for(var i = 0; i < NUM; i++){
    var length = Math.random() * 1.0 + 2.0;
    var angle = Math.random() * Math.PI * 2.0;
    speedX[i] = Math.cos(angle) * length;
    speedY[i] = Math.sin(angle) * length;
  }
}

//二点間の距離
function getLength(x1 , y1 , x2 , y2 ) {
  var pointLength;
  pointLength = Math.sqrt(Math.pow(x2 - x1 , 2 )+Math.pow(y2 - y1 , 2 )); 
  return pointLength;
}

//画面左上からのマウス座標を求める
function adjustXY(e) {
  var rect = e.target.getBoundingClientRect();
  mouseX = e.clientX - rect.left;
  mouseY = e.clientY - rect.top;
}
  • 色はきれいだけど動きがちょっと芋虫っぽい…。でもマウスでつかんで投げるとおもしろいのね。画面の外に投げたらみんな飛んでって誰もいなくなっちゃった。 クリックしたら戻ってきたけど。

  • 色がきれいでいいですね。
    僕の環境(Windows 7/Chrome)だと、マウス(実際にはトラックボールですが)でクリックしただけだと、吸着はしないで、パーティクルの運動する方向が変わるようです。
    ほんの少しでもドラッグしてやると、吸着するようです。
    こういうものはやはり、PCのグラフィックカードの性能を高く要求するのでしょうかね。
    コードも示していただけて、勉強になります。
    実際に動くものを見せてもらった後だと、興味がぐんとわきます。
    「ばねの動き」をどうやって計算しているのかが分からないので、コードをじっくり読んでみます。

  • OSやブラウザによって、微妙に挙動変わってしまうのですね。このあたり、今後Canvasでより本格的なものを作る際にいろいろ気をつける必要ありそうです…

  • 「ちょっと修正 (19:30)」のためでしょうか、今は手元のWindows 7/Chromeで閲覧した状態で、ドラッグしなくてもクリックしただけで吸着するようになっています。
    それでソースを見ているのですが、修正箇所がどこか分からず、今度はなぜクリックしただけで吸着するようになったのか、分かりません。
    よろしければ、お手すきの時にでも教えていただけると幸いです。

    また、それとは別のことですが、
    function mouseMoveListner(e) {  //画面左上からのマウス座標取得  adjustXY(e);  //マウスを移動中に  mouseMoved = true;}でtrueにしている、mouseMovedという変数は、なくても同じように動作するように思いますが、いかがでしょうか。