js制御canvasで実現したwindows上のラジオ、マルチ選択、ドラッグ制御を模したtoyプログラム


機能には、マウスクリックラジオ、ドラッグマルチ選択、ctrl+クリックコンビネーション効果、選択した単一または複数のcanvasレイヤをマウスドラッグ、方向キー移動、delete削除レイヤなどがあります.
複雑な感じがするのは主に制御ロジックで、以下はすべてのコード(注釈付きですよ)で、htmlファイルに直接コピーして保存してブラウザで効果を見ることができます
<!DOCTYPE>
<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<!--[if lt IE 9]><script language="javascript" type="text/javascript" src="/jqplot/excanvas.min.js"></script><![endif]-->
<script type="text/javascript">
	var list=[];
	var currentC;
	var selected=[];
	var _e={};
	var showTimer;
	var keyBoardTimer;
	var move=false;
	var hit=false;
	var inZoom=false;
	var exist=false;
	var ctrlDown=false;
	var directDown='';
    var cricle = function(x,y,r){
    	this.x=x;
    	this.y=y;
    	this.r=r;
		this.angle=10;
		this.posB={};

    	this.isCurrent=false;
		/*     flag  ,            ,               */
    	this.drawC=function(ctx,x,y,posA){
    		ctx.save();
    		ctx.beginPath();
    		ctx.moveTo(this.x,this.y-this.r);
			ctx.arc(this.x,this.y,this.r,2*Math.PI,0,true);
			/*          */
			for(var i=selected.length;i--;){
				if(this===selected[i]){
					exist=true;
					console.log('exits');
				}
			}
			console.log('exitfor');
			/*1.     ,  posA  ,
			*2.     x,y    .(    ) 
			*/
			if (!posA && x && y && this.isCurrent) {
					ctx.fillStyle = '#ff0000';
					currentC=this;
					this.isCurrent=true;
					if(selected.length>0){
						console.log(selected.length);
						
						if(!exist){
							console.log('notexits');
							selected.push(this);
						}
						exist=false;/*    ,             */
					}else{
						selected.push(this);
					}
			}else{
				
				if(posA){/*     ,    */
					//console.log('1');
					/*    posB:{        ,     ,    }*/
					this.posB={'x1':(this.x-this.r),'y1':(this.y-this.r),'x2':(this.x+this.r),'y2':(this.y+this.r),'x3':(this.x+this.r),'y3':(this.y-this.r),'x4':(this.x-this.r),'y4':(this.y+this.r),'x':this.x,'y':this.y,'angle':this.angle};
					/*     posB:{        ,        ,     ,    }*/
					//this.posB={x1,y1,x2,y2,x3,y3,x4,y4,x,y,angle}
					var flag=testCollision(posA,this.posB);
					if(flag){
						//console.log('2');
						
						if(selected.length>0){
							
							if(!exist){
								console.log('notexits');
								selected.push(this);
							}
							exist=false;/*    ,             */
						}else{
							selected.push(this);
						}
						
						console.log('selected',selected.length);
						ctx.fillStyle = '#ff0000';
					}else{
						//console.log('3');
						ctx.fillStyle = '#999999';
					}
				}else{/*    ,    。(      ,    )*/
					if(exist){
						ctx.fillStyle = '#ff0000';
					}else{
						ctx.fillStyle = '#999999';
					}
					exist=false;/*    ,             */
				}

			}
			ctx.fill();
			ctx.restore();
    	}
		/*        ,          */
		this.testHit=function(ctx,x,y){
			/*           */
			if(currentC){
				currentC.isCurrent=false;
				currentC=null;
			}
			ctx.save();
			ctx.beginPath();
    		ctx.moveTo(this.x,this.y-this.r);
			ctx.arc(this.x,this.y,this.r,2*Math.PI,0,true);
			if (ctx.isPointInPath(x, y)){
				hit=true;
				currentC=this;
				this.isCurrent=true;

			}
			ctx.restore();
		}
    
    }
	function draw(action){
		console.log('draw func');
		var canvas = document.getElementById('tutorial');
		canvas.height = canvas.height;//       ,      
		if (canvas.getContext){
			var ctx = canvas.getContext('2d');
			console.log(['directDown',directDown]);
			if(action===undefined){/*          */
				for(var i=0;i<10;i++){
					var c=new cricle(20*i,20*i,5*i);
					c.drawC(ctx);//         
					list.push(c);
				}
			}else{/*         */
				console.log('delete');
				for(var i=0;i<list.length;i++){
					var c=list[i];
					c.drawC(ctx);
				}
			}
		}
	}
	
	function reDraw(e,posA){// flag          
		console.log('reDraw func');
		if(e){
			e=e||event;
			var canvas = document.getElementById('tutorial');
			var x = e.clientX - canvas.offsetLeft;
			var y = e.clientY - canvas.offsetTop;
		}
		/*          selected,            */
		if(posA){
			selected=[];
		}
		canvas.height = canvas.height;//       ,      
		//var ctx = canvas.getContext('2d');
		//ctx.clearRect(0,0,canvas.width,canvas.height);
		if (canvas.getContext){
			var ctx = canvas.getContext('2d');
			for(var i=0;i<list.length;i++){
				var c=list[i];
				if(posA){
					c.drawC(ctx,x,y,posA);//   
				}else{
					console.log('ininin');
					c.drawC(ctx,x,y);//    :    、  、         
				}
			}
		}
	}
	
	function show(e){
		console.log('show func');
		
		move=true;
		e=e||event;
		var canvas = document.getElementById('tutorial');
		var ctx = canvas.getContext('2d');
		var x = e.clientX - canvas.offsetLeft;
		var y = e.clientY - canvas.offsetTop;
		var _x = _e.clientX - canvas.offsetLeft;
		var _y = _e.clientY - canvas.offsetTop;
		//showTimer=setInterval(function(e){reDraw(e);console.log('showTimer');},10,_e);//          
		/*        ,            ,    showTimer        
		*         ,   showTimer   ,            
		*/

		if(!hit){
			console.log('not hit');
			console.log('dragrect');
			//clearInterval(showTimer);
			dragRect(e,_e,__e);
		}else{
			if(selected.length>1){/*     */
				if(inZoom){
					/*      */
					console.log('length>1 inzoom');
					for(var i=selected.length;i--;){
						var c=selected[i];
						c.x=c.x+(x-_x);
						c.y=c.y+(y-_y);
					}
					reDraw(e);
				}else{
					/*    ,      */
					console.log('length>1 notinzoom');
					selected=[];
					if(currentC){
						currentC.x=currentC.x+(x-_x);
						currentC.y=currentC.y+(y-_y);
						reDraw(e)
					}
				}
			}else{/*      */
				/*      */
				console.log('length<=1 inzoom');
				if(currentC){
					currentC.x=currentC.x+(x-_x);
					currentC.y=currentC.y+(y-_y);
					reDraw(e)
				}
			}
		}
		_e=e;
	}
	function dragRect(e,_e,__e){
		console.log('dragRect func');
		var pos={};
		var canvas = document.getElementById('tutorial');
		canvas.style.zIndex='100';
		//      
		var x = e.clientX - canvas.offsetLeft;
		var y = e.clientY - canvas.offsetTop;
		//          
		var _x = _e.clientX - canvas.offsetLeft;
		var _y = _e.clientY - canvas.offsetTop;
		//        
		var __x = __e.clientX - canvas.offsetLeft;
		var __y = __e.clientY - canvas.offsetTop;
		pos.x1=x;
		pos.y1=y;
		pos.x2=__x;
		pos.y2=__y;
		reDraw(e,pos);
		var context=canvas.getContext("2d");
		//context.save();

//		context.clearRect(__x,__y,(_x-__x),(_y-__y));
//		reDraw(e,pos);
//		context.fillStyle="rgba(10%,25%,10%,0.1)";  //     
//		context.strokeStyle="000";  //    
//		context.linewidth=1;  //   
//		context.fillRect(x,y,(__x-x),(__y-y));  //     x y      
		//context.strokeRect(x,y,(__x-x),(__y-y));  //     x y      
		//context.restore();
		
		context.beginPath();
        context.setLineDash([5,2]);
        context.rect(__x,__y,(_x-__x),(_y-__y));
		context.stroke();

	}

	window.onload=function(){
		
		var canvas = document.getElementById('tutorial');
		draw();//          ,(x,y)      ,    
		
		canvas.onmousedown=function(e){
			move=false;
			e=e||event;
			var x = e.clientX - canvas.offsetLeft;
			var y = e.clientY - canvas.offsetTop;
			//if(currentC)	currentC.isCurrent=false;//      ,       
			//currentC=null;//      
			/*      ,          */
			if (canvas.getContext){
				var ctx = canvas.getContext('2d');
				for(var i=list.length;i--;){
					var c=list[i];
					c.testHit(ctx,x,y);
					if(hit) break;
				}
			}
			/*            ,  selected    0       */
			if(selected.length>0){
				for(var i=selected.length;i--;){
					var c=selected[i];
					if(currentC===selected[i]){
						inZoom=true;
						break;
					}
				}
			}
			/*           ctrl */
			if(hit && !ctrlDown){
				
				if(inZoom){/*          */
					console.log(selected.length);
					console.log('inZoom');
					/*    ,         (   hit     ),       :    ,      */

				}else{/*           */
					/*        :    ,      */
					console.log('notinZoom');
					selected=[];
					reDraw(e);
				}
			}else{
				/*   ,     ctrl ,      ,        */
				//selected=[];
				
			}
			
			_e=e;
			__e=e;
			
			//showTimer=setInterval(function(e){reDraw(e);},10,_e);//          
			canvas.onmousemove=show;//              

			document.onmouseup=function(){
				/*        ,     ,             */
				if(!move){
					if(!hit){/*     ,      、    */
						if(currentC){
							currentC.isCurrent=false;
							currentC=null;
						}
						selected=[];
					}else{/*     */
						hit=false;/*      */
						if(ctrlDown){/*     ctrl*/
							if(!inZoom){/*        selected ,   ,   selected   ,       */
								selected.push(currentC);
							}else{
								if(currentC){
									currentC.isCurrent=false;
									currentC=null;
								}
								console.log(['splice',selected.length]);
								selected.splice(i,1);
								console.log(['splice',selected.length]);
							}
						}else{/*      ctrl,    selected,      */
							selected=[];
						}
					}
					inZoom=false;
					
					reDraw(e);
				}else{
					move=false;/*     ,      */
					hit=false;/*     ,      */
					inZoom=false;/*     ,           */
					
				}
				//     ,         
				//      ,           
				canvas.onmousemove=null;//                 
				//reDraw(e);
				clearInterval(showTimer);//         
				console.log('up');
			}

		}
		document.onkeydown=function(e){
			var ev = window.event || e;
			
			console.log(['keydown',ev.keyCode]);
			switch(ev.keyCode){
				case 17:
					
					ctrlDown=true;
					break;
				case 37:
					ev.preventDefault();
					directDown=true;
					keyboardMove(-1,0);
					break;
				case 38:
					directDown=true;
					ev.preventDefault();
					keyboardMove(0,-1);
					break;
				case 39:
					directDown=true;
					ev.preventDefault();
					keyboardMove(1,0);
					break;
				case 40:
					directDown=true;
					ev.preventDefault();
					keyboardMove(0,1);
					break;
				case 46:
					keyboardDelete();
					break;
			}
		}
		document.onkeyup=function(e){
			var ev=window.event || e;
			//ev.preventDefault();
			console.log(['keyup',ev.keyCode]);
			switch(ev.keyCode){
				case 17:
					ctrlDown=false;
					break;
				case 37:
				case 38:
				case 39:
				case 40:
					directDown=false;
					break;
				case 46:

			}
		}
	}
	/*         */
	function keyboardMove(x,y){
		if(selected.length>0){
			for(var i=selected.length;i--;){
				var c=selected[i];
				c.x+=x;
				c.y+=y;
			}
		}else if(currentC){
			currentC.x+=x;
			currentC.y+=y;
		}
		draw('direct');
	}
	/*delete     */
	function keyboardDelete(){
		/*        */
		var tempList=[];
		if(selected.length>0){
			for(var j=list.length;j--;){
				var flag=true;
				for(var i=selected.length;i--;){
					if(list[j]===selected[i]){
						flag=false;
						break
					}					
				}
				if(flag){
					tempList.push(list[j]);
				}
			}
			list=tempList;
		}else if(currentC){
			for(var j=list.length;j--;){
				if(list[j]===currentC){
					list.splice(j,1);
					break
				}
			}
		}
		draw('delete');
	}
	/*    */
	function testCollision(posA,posB){
		//console.log(['posA',posA]);
		//console.log(['posB',posB]);
		var crossA=lineSpace(posA.x1,posA.y1,posA.x2,posA.y2);
		var crossB=lineSpace(posB.x1,posB.y1,posB.x2,posB.y2);
		var centerB={};
		var crossPos={};
		var centerA={};
		var max={};
		var min={};
		var sh={};
		var flag=false;
		centerA.x=((posA.x1-posA.x2)>0)?(posA.x1-(posA.x1-posA.x2)/2):(posA.x2-(posA.x2-posA.x1)/2);
		centerA.y=((posA.y1-posA.y2)>0)?(posA.y1-(posA.y1-posA.y2)/2):(posA.y2-(posA.y2-posA.y1)/2);
		var centerAToB=lineSpace(centerA.x,centerA.y,posB.x,posB.y);
		if(centerAToB>((crossA+crossB)/2)){
			//console.log('4');
			return flag;
		}
		if(posB.angle===0){
			//console.log('5');
			/*           */
			/*crossPos:{    :   ,   ,    :   ,   }*/
			if(posA.x1>posA.x2 && posA.y1>posA.y2){
				/*     */
				crossPos={'x1':posB.x1,'y1':posB.y1,'x2':posB.x2,'y2':posB.y2,'x3':posA.x2,'y3':posA.y2,'x4':posA.x1,'y4':posA.y1};
				flag=simpleTest(crossPos);
				return flag;
			}else if(posA.x1<posA.x2 && posA.y1<posA.y2){
				/*     */
				//console.log('6');
				crossPos={'x1':posB.x1,'y1':posB.y1,'x2':posB.x2,'y2':posB.y2,'x3':posA.x1,'y3':posA.y1,'x4':posA.x2,'y4':posA.y2};
				flag=simpleTest(crossPos);
				//console.log(flag);
				return flag;
			}else if(posA.x1<posA.x2 && posA.y1>posA.y2){
				/*     */
				var x1=posA.x1;
				var y1=posA.y2;
				var x2=posA.x2;
				var y2=posA.y1;
				crossPos={'x1':posB.x1,'y1':posB.y1,'x2':posB.x2,'y2':posB.y2,'x3':x1,'y3':y1,'x4':x2,'y4':y2};
				flag=simpleTest(crossPos);
				return flag;

			}else if(posA.x1>posA.x2 && posA.y1<posA.y2){
				/*     */
				var x1=posA.x2;
				var y1=posA.y1;
				var x2=posA.x1;
				var y2=posA.y2;
				crossPos={'x1':posB.x1,'y1':posB.y1,'x2':posB.x2,'y2':posB.y2,'x3':x1,'y3':y1,'x4':x2,'y4':y2};
				flag=simpleTest(crossPos);
				return flag;
			}

		}else{
			//console.log('      ');
			/*          */
			/*        x、y    ,    */
			max.Bx1=Math.max(posB.x1,posB.x2,posB.x3,posB.x4);
			max.By1=Math.max(posB.y1,posB.y2,posB.y3,posB.y4);
			min.Bx1=Math.min(posB.x1,posB.x2,posB.x3,posB.x4);
			min.By1=Math.min(posB.y1,posB.y2,posB.y3,posB.y4);
			max.Ax1=Math.max(posA.x1,posA.x2);
			max.Ay1=Math.max(posA.y1,posA.y2);
			min.Ax1=Math.min(posA.x1,posA.x2);
			min.Ay1=Math.min(posA.y1,posA.y2);


			if(max.Bx1<min.Ax1 || max.Ax1<min.Bx1 || max.By1<min.Ay1 || max.Ay1<min.By1){
				return false;
			}

			
			/*                      */
			/*  B14    */
			sh.x=posB.y4-posB.y1;
			sh.y=posB.x1-posB.x4;
			sh.x1=posA.x1-posA.x3;
			sh.y1=posA.y1-posA.y3;
			var lineA1=getShadow(sh);
			sh.x1=posA.x1-posA.x4;
			sh.y1=posA.y1-posA.y4;
			var lineA2=getShadow(sh);
			sh.x1=posA.x2-posA.x3;
			sh.y1=posA.y2-posA.y3;
			var lineA3=getShadow(sh);
			sh.x1=posA.x2-posA.x4;
			sh.y1=posA.y2-posA.y4;
			var lineA4=getShadow(sh);
			var maxB=lineSpace(posB.x1,posB.y1,posB.x3,posB.y3);
			var maxA=Math.max(lineA1,lineA2,lineA3,lineA4);
			var minA=Math.min(lineA1,lineA2,lineA3,lineA4);
			if(maxB<minA || maxA<0){
				return false;
			}
			/*  B13    */
			sh.x=posB.y3-posB.y1;
			sh.y=posB.x1-posB.x3;
			sh.x1=posA.x1-posA.x3;
			sh.y1=posA.y1-posA.y3;
			var lineA1=getShadow(sh);
			sh.x1=posA.x1-posA.x4;
			sh.y1=posA.y1-posA.y4;
			var lineA2=getShadow(sh);
			sh.x1=posA.x2-posA.x3;
			sh.y1=posA.y2-posA.y3;
			var lineA3=getShadow(sh);
			sh.x1=posA.x2-posA.x4;
			sh.y1=posA.y2-posA.y4;
			var lineA4=getShadow(sh);
			var maxB=lineSpace(posB.x1,posB.y1,posB.x4,posB.y4);
			var maxA=Math.max(lineA1,lineA2,lineA3,lineA4);
			var minA=Math.min(lineA1,lineA2,lineA3,lineA4);
			if(maxB<minA || maxA<0){
				return false;
			}


			return true;
		}


	}
	/*shadow:{   :x,y,    :x1,y1}*/
	function getShadow(sh){
		var temp1,temp2,length;
		temp1=sh.x*sh.y1+sh.y*sh.x1;
		temp2=Math.sqrt(sh.x*sh.x+sh.y*sh.y);
		length=temp1/temp2;
		return length;
	}
	function simpleTest(crossPos){
		//console.log(['crossPos',crossPos]);
		/*   */
		var maxx=Math.max(crossPos.x1,crossPos.x3);
		var maxy=Math.max(crossPos.y1,crossPos.y3);
		/*   */
		var minx=Math.min(crossPos.x2,crossPos.x4);
		var miny=Math.min(crossPos.y2,crossPos.y4);
		if(maxx>minx || maxy>miny){
			return false;
		}else{
			return true;
		}
	}

	function pointToLine(x0,y0,x1,y1,x2,y2){
		var space=0;
		var a=lineSpace(x1,y1,x2,y2);//    
		var b=lineSpace(x0,y0,x1,y1);//(x1,y1)     
		var c=lineSpace(x0,y0,x2,y2);//(x2,y2)     
		
		if(c+b===a){
			space=0;
			return space;
		}
		if(a<=0.000001){
			alert('     ');
			return;
		}
		var p=(a+b+c)/2;
		var s=Math.sqrt(p*(p-a)*(p-b)*(p-c));
		space=2*s/a;
		return space;
	}
	function lineSpace(x1,y1,x2,y2){
		var lineLength=Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
		return lineLength;
	}
</script>
<style type="text/css">
  canvas { border: 1px solid black; }
</style>
</head>
<body style="background:#eeeeee;">
    <canvas id="tutorial" width="1000" height="550" style="z-index:100;display:block;position:absolute;"></canvas>
</body>
</html>