テニスのようなゲーム

テニスのようなゲームです。
ソースコード

<!DOCTYPE html>
<html lang="ja">
<HEAD>
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<script src="http://api.html5media.info/1.1.4/html5media.min.js" type="text/javascript"></script> 
<script src="https://shiba-sub.sakuraweb.com/wp-content/themes/twentyeleven/js/flashcanvas.js" type="text/javascript"></script>
<![endif]-->
<meta charset="Shift_JIS"> 
<meta http-equiv="Content-Script-type" content="text/javascript">
<TITLE>テニス</TITLE>
<style>
p      {
       font-size: 20px;
}
canvas {
       background-color: green;
       zoom: 0.8;
}
</style>
</HEAD>
<BODY>
<p id="scoreboard">0</p>
<canvas id="canvas1" width="300" height="400"></canvas>
<form>
<input type="button" value="開始" onclick="tstart()">
</form>
<p id="message"></p>
<script>
var scoreboard;
var message;
var canvas;
var contex;
var canvasrect;
var width;
var height;
var courtrect;
var ball;
var racket;
var score;
var timerid;
var dxd;
var nowMagniFication;
var chgZoom;
var ajust = 2;
window.onload = init;

// 初期処理
function init() {
  dxd=screen.deviceXDPI;
  nowMagniFication=dxd/96;
  chgZoom=nowMagniFication;
  zoom = 1.25/chgZoom;
  document.body.style.zoom = zoom;
  scoreboard = document.getElementById("scoreboard");
  message = document.getElementById("message");
  canvas = document.getElementById("canvas1");
  context = canvas.getContext("2d");
  context.fillStyle = "springgreen";
  context.font = "20px serif";
  context.textAlign = "center";
  canvasrect = canvas.getBoundingClientRect();
  width = canvas.width;
  height = canvas.height;
  courtrect = new Rectangle(0, 0, width, height);
  ball = new Ball(14, 14, 3);
  racket = new Racket(width/2, height-40, 40, 10);
  score = 0;
  timerid = -1;
  document.attachEvent("onmousemove", function (event) {
      racket.mousemoved(event);
  });
}

function Ball(width, height, step) {
  this.rect = new Rectangle(0, 0, width, height);
  this.step = step; // 描画ごとの移動距離
  this.r = width / 2; // 半径
  this.direction = "se"; // 移動方向
  this.reset = function() {
    this.rect.x = 0;
    this.rect.y = 0;
    this.direction = "sw";
  };
  this.move = function(x, y) {
     this.clear();
     this.rect.x += this.step * x;
     this.rect.y += this.step * y;
     context.fillStyle = "yellow";
     this.draw();
  };
  this.moveDirection = function() {
    switch (this.direction) {
      case "se": // 右下
         this.move(1, 1);
         break;
      case "sw": // 左下
         this.move(-1, 1);
         break;
      case "nw": // 左上
         this.move(-1, -1);
         break;
      case "ne": // 右上
         this.move(1, -1);
         break;
    }
  };
  this.turn = function() {
    switch (this.rect.outside(courtrect)) {
    case "west":
      if (this.direction == "sw") {
        this.direction = "se";
      }
      else if (this.direction == "nw") {
        this.direction = "ne";
      }
      break;
    case "east":
      if (this.direction == "se") {
        this.direction = "sw";
      }
      else if (this.direction == "ne") {
        this.direction = "nw";
      }
      break;
    case "north":
      if (this.direction == "ne") {
        this.direction = "se";
      }
      else if (this.direction == "nw") {
        this.direction = "sw";
      }
      break;
    }
  };
  this.hit = function() {
    if (this.south() && this.rect.collision(racket.rect)) {
      scoreboard.innerHTML = ++score;
      if (this.direction == "se") {
        this.direction = "ne";
      }
      else if (this.direction == "sw") {
        this.direction = "nw";
      }
    }
  };
  this.south = function() {
    return this.direction == "se" || this.direction == "sw";
  };
  this.beyondSouth = function() {
    return this.rect.outside(courtrect) == "south";
  };
  this.clear = function() {
    context.clearRect(this.rect.x, this.rect.y,this.rect.width,
                      this.rect.height);
  }
  this.draw = function() {
    context.beginPath();
    context.arc(this.rect.x + this.r,
                this.rect.y + this.r,
                this.r, 0, Math.PI*2, false);
    context.fill();
  }
}

function Racket(x, y, width, height) {
  this.rect = new Rectangle(x, y, width, height);
  this.mousemoved = function(event) {
    this.clear();
    this.rect.x = event.clientX - canvasrect.left - this.rect.width / 2;
    this.draw();
  };
  this.clear = function() {
    context.clearRect(this.rect.x, this.rect.y,
                      this.rect.width, this.rect.height);
  };
  this.draw = function() {
    context.fillStyle = "silver";
    context.fillRect(this.rect.x, this.rect.y,
                     this.rect.width, this.rect.height);
  };
}

function Rectangle(x, y, width, height) {
  this.x = x; // 左上の頂点のx 座標
  this.y = y; // 左上の頂点のy 座標
  this.width = width; // 横の長さ
  this.height = height; // 縦の長さ
  this.outside = function(rect) {
    if (this.x < rect.x) {
       return "west";
    }
    else if (this.x + this.width > (rect.x + rect.width)) {
       return "east";
    }
    else if (this.y < rect.y) {
       return "north";
    }
    else if (this.y + this.height > (rect.y + rect.height)) {
       return "south";
    }
    else {
       return "inside";
    }
  };
  this.collision = function(rect) {
    return this.overlap(this.x, this.width,rect.x, rect.width) &&
           this.overlap(this.y, this.height,rect.y, rect.height);
  };
  this.overlap = function(a, alen, b, blen) {
    if (a < b) {
       return b - ajust <= a + alen;
    }
    else {
       return a - ajust <= b + blen;
    }
  };
}

function gameOver() {
  ball.clear();
  message.innerHTML = "ゲームオーバー";
  clearInterval(timerid);
  timerid = -1;
}

function tstart() {
  if (timerid == -1) {
     message.innerHTML = "";
     scoreboard.innerHTML = "0";
     ball.reset();
     score = 0;
     timerid = setInterval(function() {
       if (ball.beyondSouth()) {
          gameOver();
       }
       else {
          ball.turn();
          ball.hit();
          ball.moveDirection();
       }
     }, 25);
  }
}
</script>
</BODY>
</HTML>

実行はこちらから