アットウィキロゴ
HTML5 の canvas を練習するために作りました。
マウスでキャンバス上のボールを転がすアプリです。

使い方

キャンバス上を適当にクリックすると、赤いボールがクリックされた方向へ転がります。
ボールは徐々に減速し、最後には止まります。

使用している技術

html5 の canvas
javascript

【バグ】
  • たまに、ボールが壁をすり抜けてしまう。

以下、ソースを掲載します。
  • bound.html

<!DOCTYPE html>
<html lang = "ja" >
  <head>
    <meta charset = "UTF-8">
    <title> アニメーションサンプル </title>
    <style type = "text/css">
      <!-- キャンバスの定義 -->
      #id_canvas
      {
        position :  absolute;
        top      :     0px;
        left     :     0px;
        z-index  :         1;
      }
    </style>
    <script src = "./utility.js" ></script>
    <script type = "text/javascript" >
// --------------------------
// ---- コールバック関数 ----
// --------------------------
// マウスクリック時に呼ばれる関数
mouseClkEv = function mouseClickEvent( e_ )
{
  var rect = e_.target.getBoundingClientRect();
  var mouseClickX = e_.clientX - rect.left;
  var mouseClickY = e_.clientY - rect.top;
  ball.dirTo( mouseClickX, mouseClickY );
  ball.addVelocity( 10 );
}
// マウスが動いた時に呼ばれる関数
mouseMoveEv = function mouseMoveEvent( e_ )
{
  canvas = document.getElementById("id_canvas");
  context = canvas.getContext("2d");

  var rect = e_.target.getBoundingClientRect();
  var mouseMoveX = e_.clientX - rect.left ;
  var mouseMoveY = e_.clientY - rect.top ;

  textArea = document.getElementById("coodination");
  textArea.innerHTML = "("
    + mouseMoveX
    + ", "
    + mouseMoveY
    + ")";
}
// キャンバスを再描画する関数
function redrawAll( _obstacles, _ball )
{
  canvas = document.getElementById("id_canvas");
  context = canvas.getContext("2d");

  context.clearRect( 0, 0, width + 10, height + 10 );
  drawAll( _obstacles, _ball );
}
function drawAll( _obstacles, _ball )
{
  drawObstacles( _obstacles );
  drawBall( _ball );
}
function drawWalls( _walls )
{
  canvas = document.getElementById("id_canvas");
  context = canvas.getContext("2d");

  context.beginPath();
  context.strokeStyle = "rgba(0, 0, 0, 0.5)";
  for( i = 0; i < _walls.length; i++ )
  {
    context.moveTo(
      _walls[i].tailX_, _walls[i].tailY_
      );
    context.lineTo(
      _walls[i].headX_, _walls[i].headY_
      );
  }
  context.stroke();
}
function drawObstacles( _obstacles )
{
  for( j = 0; j < _obstacles.length; j++ )
  { drawWalls( _obstacles[j] ); }
}
function drawBall( ball_ )
{
  canvas = document.getElementById("id_canvas");
  context = canvas.getContext("2d");

  context.beginPath();
  context.strokeStyle = "rgba(255, 0, 0, 0.5)";
  context.arc(
      ball_.curX_,
      ball_.curY_,
      5,
      0,
      360
      );
  context.stroke();
}
// ----------------------------
// ---- アニメーション関連 ----
// ----------------------------
// 0.1 秒毎に呼び出される関数
// 0.1 秒毎に ball 動かし, 衝突判定と再描画をします.
// ball は 0.1 秒毎に少しずつ減速します.
var delta = 0.1;
function action()
{
  ball.gensoku( delta ); // 減速
  ball.moveTo( delta ); // 動かす
  collision( obstacles, ball, delta ); // 衝突判定
  redrawAll( obstacles, ball ); // 再描画
}

onload = function init() 
{
  drawAll( obstacles, ball );
  // キャンバスへのマウスイベントの登録
  canvas = document.getElementById("id_canvas");
  context = canvas.getContext();
  canvas.addEventListener( "click", mouseClkEv, false );
  canvas.addEventListener( "mousemove", mouseMoveEv, false );
  // 100 ミリ秒毎に, action() 関数が呼ばれるに, 関数を登録.
  setInterval( "action()", 1000 * delta );
}

// インスタンスの定義
// 障害物の生成
var obstacles = new Array(0);
// 外壁の生成
var width = 500;
var height = 500;
var field = new Array(4);
field[0] = new Wall( 0, 0, 0, height );
field[1] = new Wall( 0, height, width, height );
field[2] = new Wall( width, height, width, 0 );
field[3] = new Wall( width, 0, 0, 0 );
obstacles.push( field );

// ボールの生成
// 初期位置は 250, 250
var ball = new Ball( 250, 250 );
    </script>
  </head>
  <body>
    キャンバス↓
    <div id = "coodination" ></div>
    <canvas id = "id_canvas" width = 1200 height = 1200 ></canvas>
    <menu type = "toolbar">
  </body>
</html>

  • utility.js
// 壁にボールが当たって跳ね返る処理を記述したコードです。
// HTML5 の機能とは関係ないのですが, 掲載いたします。
// ----------------------
// ---- 数学いろいろ ----
// ----------------------
function normalize( _x, _y )
{
  d = Math.sqrt( innerProduct( _x, _y, _x, _y ) );
  var ret = new Array(2);
  ret[0] = _x / d;
  ret[1] = _y / d;
  return ret;
}
function computeNormalVector( _x, _y, _theta )
{
  var ret = new Array(2);
  ret[0] =   Math.cos( _theta ) * _x + Math.sin( _theta ) * _y;
  ret[1] = - Math.sin( _theta ) * _x + Math.cos( _theta ) * _y;
  return normalize( ret[0], ret[1] );
}
function innerProduct( _ux, _uy, _vx, _vy )
{ return ( _ux * _vx ) + ( _uy * _vy ); }
function computeRadian( _t ) { return _t * Math.PI / 180.0; }
// ABC が左回りならば, 正となる符号付き面積.
function signedArea(
  _ax, _ay,
  _bx, _by,
  _cx, _cy
  )
{
  temp1 = ( _bx - _ax ) * ( _cy - _ay );
  temp2 = ( _cx - _ax ) * ( _by - _ay );
  return temp1 - temp2;
}
function isIntersected(
  _ax, _ay,
  _bx, _by,
  _cx, _cy,
  _dx, _dy
  )
{
  abc = signedArea( _ax, _ay, _bx, _by, _cx, _cy );
  abd = signedArea( _ax, _ay, _bx, _by, _dx, _dy );
  cda = signedArea( _cx, _cy, _dx, _dy, _ax, _ay );
  cdb = signedArea( _cx, _cy, _dx, _dy, _bx, _by );
  temp1 = abc * abd;
  temp2 = cda * cdb;
  if( ( temp1 < 0.0 ) && ( temp2 < 0.0 ) )
  { return true; }
  else
  { return false; }
}
function intersection(
  _ax, _ay,
  _bx, _by,
  _cx, _cy,
  _dx, _dy
  )
{
  var n = new Array(2);
  var m = new Array(2);
  var a = new Array(2);
  var b = new Array(2);

  n = normalize( _ax - _bx, _ay - _by );
  a[0] = _ax; a[1] = _ay;
  m = normalize( _cx - _dx, _cy - _dy );
  b[0] = _cx; b[1] = _cy;
  beta = ( ( n[1] * ( a[0] - b[0] ) ) - ( n[0] * ( a[1] - b[1] ) ) )
    / ( ( n[1] * m[0] ) - ( n[0] * m[1] ) );

  var ret = new Array(2);

  ret[0] = ( m[0] * beta ) + b[0];
  ret[1] = ( m[1] * beta ) + b[1];
  
  return ret;
}
// ----------------
// ---- ボール ----
// ----------------
function Ball( _startX, _startY )
{
  this.curX_ = _startX; 
  this.curY_ = _startY; 
  this.preX_ = _startX;
  this.preY_ = _startY;
  this.dirX_ = 1;
  this.dirY_ = 0;
  this.velocity_ = 0.0;  // 速度( pix/sec )
  this.acceleration_ = -4;  // 加速度( pix/sec^2 )
  
  this.dirTo  = function( _x, _y )
  {
    var temp = new Array(2);
    temp = normalize( _x - this.curX_, _y - this.curY_ );
    this.dirX_ = temp[0];
    this.dirY_ = temp[1];
  }
  this.moveTo = function( sec_ )
  {
     this.preX_ = this.curX_;
     this.preY_ = this.curY_;
     this.curX_ += this.dirX_ * sec_ * this.velocity_;
     this.curY_ += this.dirY_ * sec_ * this.velocity_;
  }
  this.gensoku = function( sec_ )
  {
    if( this.velocity_ > 0 )
    { this.velocity_ += sec_ * this.acceleration_; }
  }
  this.addVelocity = function( iV_ )
  {
    this.velocity_ += iV_;
  }
}
// ------------
// ---- 壁 ----
// ------------
function Wall( _tailX, _tailY, _headX, _headY )
{
   this.tailX_ = _tailX;
   this.tailY_ = _tailY;
   this.headX_ = _headX;
   this.headY_ = _headY;

   this.collision = function( _ball )
   {
      var normalVector = new computeNormalVector( 
            this.headX_ - this.tailX_,
            this.headY_ - this.tailY_,
            computeRadian( 90.0 ) );
      var tempVector = new Array(2);
      tempVector[0] = -_ball.dirX_;
      tempVector[1] = -_ball.dirY_;
      var a = innerProduct(
            normalVector[0],
            normalVector[1],
            tempVector[0],
            tempVector[1] );
      _ball.dirX_ = ( 2.0 * a * normalVector[0] ) - tempVector[0];
      _ball.dirY_ = ( 2.0 * a * normalVector[1] ) - tempVector[1];
  }
}
function collision( _obstacles, _ball, _sec )
{
  for( j = 0; j < _obstacles.length; j++ )
  {
    for( i = 0; i < _obstacles[j].length; i++ )
    {
      temp = isIntersected(
          _ball.preX_, _ball.preY_,
          _ball.curX_, _ball.curY_,
          _obstacles[j][i].tailX_, _obstacles[j][i].tailY_,
          _obstacles[j][i].headX_, _obstacles[j][i].headY_
          );
      if( temp == true )
      {
        _obstacles[j][i].collision( _ball );

        var intCoord = new Array(2);
        intCoord = intersection(
            _ball.preX_,_ball.preY_,
            _ball.curX_,_ball.curY_,
            _obstacles[j][i].tailX_, _obstacles[j][i].tailY_,
            _obstacles[j][i].headX_, _obstacles[j][i].headY_
            );
        _ball.curX_ = intCoord[0];
        _ball.curY_ = intCoord[1];
        _ball.preX_ = intCoord[0];
        _ball.preY_ = intCoord[1];
      }
    }
  }
}
  • jsでオブジェクトとか初めて見たから新鮮… オブジェクト内のメソッドってこんな風に書くのか! -- たかぎ (2012-04-23 22:55:50)
  • ありがとうございます!実は、僕も初めてオブジェクト使って書いてみました^^ -- 鈴木泰斗 (2012-04-26 22:41:04)
名前:
コメント:
最終更新:2012年04月26日 22:41