canvas遊び

やってみたいことがあって、機能を考えると htmlのcanvas要素が使えそう。
オブジェクトに描画させて、intervalを設定してアニメーションしてマウスに反応して…と考えると、何のことはない、ゲームプログラミングでいうタスクシステムだった。
ということで実装中。

http://bitbucket.org/leather_sole/cantas/

    • タスクの登録
    • 更新間隔の設定
    • タスクの順次実行
    • mouseoverイベントに対応
  • これから
    • タスクにcanvas以外の要素を持たせる
    • タスクに描画要素を複数持たせる

今のところタスクはコードに実装しているが、動きや画像は、データとして持った方がいいんだろうな。後々考えてみよう。

javascriptは、初めてまともに触るけど、細々したところで迷う。今のも、特にスコープがおかしいが、とりあえず動かすの前提で作っている。
bitbucket.org と、ditzを使うのも初めてに近いが、こちらはさほど迷うでもなく使えている。


task.js

var Task = function(canvas_,x,y,angle){
	this.canvas = canvas_;
	this.ctx = this.canvas.getContext("2d");
	this.x=x;
	this.y=y;
	this.angle=angle;
	this.operations=[this.moveTo,this.rotate,this.draw];
	this.mouseover=false;
}; 
Task.prototype.draw= function(task){
	if (!task.mouseover){
		var ctx = task.ctx;
		ctx.fillStyle = "rgb(255,0,0)";
		ctx.fillRect (0, 0, 80, 80);
		ctx.beginPath();
		ctx.arc(75,75,50,0,Math.PI*2,false);
		ctx.fillStyle = "rgba(255,50,100,0.5)";
		ctx.fill();

		ctx.beginPath();
		ctx.arc(85,75,50,0,Math.PI*2,false);
		ctx.fillStyle = "rgba(55,250,10,0.5)";
		ctx.fill();
	}else{
		var ctx = task.ctx;
		ctx.beginPath();
		ctx.arc(75,75,70,0,Math.PI*2,false);
		ctx.strokeStyle = "green";
		ctx.stroke();
	}
};
Task.prototype.moveTo= function(task){
	var ctx = task.ctx;
	ctx.translate(task.x,task.y);
};
Task.prototype.rotate=function(task){
	var ctx = task.ctx;
	ctx.rotate(task.angle);
};
Task.prototype.nextPosition=function(x,y,angle){
	this.x=this.x + x;
	this.y=this.y + y;
	this.angle = this.angle + angle;
};
Task.prototype.show=function(){
	var ctx = this.ctx;
	ctx.save();
	this.calculate();
	for (var i = 0; i < this.operations.length; i ++){
		this.operations[i](this);
	}
	ctx.restore();
};
Task.prototype.calculate=function(){
	this.nextPosition(0,0,0.1);
};
Task.prototype.act = function(x,y,firstObj,task){
	var deltaX = x - task.x;
	var deltaY = y - task.y;
	var nx = (deltaX != 0) ? deltaX / Math.abs(deltaX) : 0;
	var ny = (deltaY != 0) ? deltaY / Math.abs(deltaY) : 0;
	task.nextPosition(nx,ny,0);
	if (-25 <= deltaX && deltaX <= 25 && -25 <= deltaY && deltaY <= 25 ){
		task.mouseover=true;
		return true;
	} else{
		task.mouseover=false;
		return false;
	}
};

task_system.js

var TaskSystem = function(canvas_){
	this.canvas = canvas_;
	this.offsets = getElementPosition(canvas_);
	this.tasks = [];
	this.timerId=0;
	this.init = function(){
		var tmp=this;
		if(this.canvas.addEventListener) { 
   this.canvas.addEventListener('mousemove', function(e){tmp.produceTasks(e,tmp);}, false);
   } else if(this.canvas.attachEvent) {
  this.canvas.attachEvent('onmousemove',function(e){tmp.produceTasks(e,tmp);});
		 }
	 }
	 this.init();
 }
 TaskSystem.prototype.clear = function(){
	 var ctx = this.canvas.getContext("2d");
	 ctx.clearRect(0, 0, parseInt(this.canvas.width), parseInt(this.canvas.height));
 };
 TaskSystem.prototype.addTask = function(task){
	 this.tasks.push(task);
 };
 TaskSystem.prototype.showTasks = function(){
	 this.clear();
	 for (var i = 0; i < this.tasks.length; i ++){
		 this.tasks[i].show();
	 }
 };

 TaskSystem.prototype.produceTasks = function(e,taskSystem){
	 var x = e.layerX-taskSystem.offsets['left'];
	 var y = e.layerY-taskSystem.offsets['top'];
	 var isFirstActor = true;
	 for (var i = 0; i < taskSystem.tasks.length; i ++){
		 isFirstActor = isFirstActor && taskSystem.tasks[i].act(x,y,isFirstActor,taskSystem.tasks[i]);
	 }
 };

 TaskSystem.prototype.startTimer = function(interval){
	 var tmp=this;
	 this.timerId = setInterval(function(){ tmp.showTasks();},interval);
 };

 TaskSystem.prototype.clearTimer = function(interval){
	 clearInterval(this.timerId);

 };
 function getElementPosition(element) {
	 var offsetTrail = (typeof element == 'string') ? document.getElementById(element) : element;
	 var offsetLeft = 0;
	 var offsetTop = 0;

	 while (offsetTrail) {
		 offsetLeft += offsetTrail.offsetLeft;
		 offsetTop += offsetTrail.offsetTop;
		 offsetTrail = offsetTrail.offsetParent;
	 }

	 if (navigator.userAgent.indexOf('Mac') != -1 && typeof document.body.leftMargin != "undefined") {
		 offsetLeft += document.body.leftMargin;
		 offsetTop += document.body.topMargin;
	 }

	 return ({left: offsetLeft, top: offsetTop});
 }

これ、予想はしてたけどunit testが役に立たないな…。

作りたいものは、例えばこれのようなもの。iCal拾ってきて動的に生成させたい。
http://kids.goo.ne.jp/wadai/img/contents/038/img-03.gif