HTML5/Heading Detection
Getting the Heading on Canvas
Soul-Learner
2012. 4. 18. 00:13
HTML5의 Canvas 상에서 이용자가 설정한 방향각을 적용하는 예
테스트 환경: HTML5, Eclipse, Google Chrome, Javascript
아래의 캔바스에서 마우스로 클릭하면 십자선이 나타나고 마우스를 이동하여 방향을 제시한 후에 다시 클릭하면 제시된 방향으로 십자선이 이동하는 예이다.
한가지 주의할 점은 Math.acos()는 코사인비(코사인값)를 받아서 각도를 리턴하는 역코사인 함수인데, 이 함수가 리턴하는 값은 Radian 각 이며 일반각(Degree)으로 변환했을 경우 0~360 사이의 수가 아니라는 점이다. 실제로 리턴하는 값은 0~180 사이의 값이므로 180도를 넘어서는 각도가 필요할 경우에는 리턴된 값을 보정하여 사용할 필요가 있다. 즉, 200 deg를 리턴해야 하는 상황에서 160도를 리턴하고 있다.
이용자가 원하는 방향으로 오브젝트를 이동하거나 미사일 등의 발사체를 발사할 경우에 응용하면 될 것이다.
<!DOCTYPE html> <html> <head> <meta charset="EUC-KR"> <title>Event Test</title> <style type="text/css"> #canvas1 { border:1px dotted black;} </style> <script type="text/javascript"> var isQueDrawing = false; var dirRad = 0; var speedX = 0; var speedY = 0; var isMoving = false; var timer = 0; var crossPt = new Point(0,0); var headPt = new Point(0,0); var tailPt = new Point(0,0); var DEG = 180/Math.PI; function Point(x, y) { this.x = x; this.y = y; Point.prototype.set=function(x,y){ this.x = x; this.y = y; } Point.prototype.copy=function(p){ return new Point(p.x, p.y); } Point.prototype.sub=function(p){ return new Point(this.x-p.x,this.y-p.y); } Point.prototype.length=function(){ return Math.sqrt(this.x*this.x+this.y*this.y); } }; window.onload = function() { canvas = document.getElementById("canvas1"); ctx = canvas.getContext("2d"); canvas.addEventListener("mousedown",onMouseDown,true); canvas.addEventListener("mousemove",onMouseMove,true); }; function onMouseDown(e){ isMoving = false; clearInterval(timer); if(crossPt.x != 0 && crossPt.y != 0 && isQueDrawing) { isQueDrawing = false; isMoving = true; findHeading(); setHeading(); timer = setInterval("gameLoop()", 10); return; } var canvasX = e.pageX - this.offsetLeft; var canvasY = e.pageY - this.offsetTop; crossPt.set(canvasX,canvasY); headPt.set(canvasX,canvasY); drawCross(); } function onMouseMove(e) { if(isMoving || (crossPt.x==0 && crossPt.y==0)) return; isQueDrawing = true; var canvasX = e.pageX - this.offsetLeft; var canvasY = e.pageY - this.offsetTop; tailPt.set(canvasX,canvasY); ctx.fillStyle = "white"; ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); ctx.fill(); findHeading(); displayHeading(); drawCross(); drawLine(); } function drawLine() { ctx.beginPath(); ctx.lineWidth = 2; ctx.strokeStyle = "black"; ctx.moveTo(headPt.x, headPt.y); ctx.lineTo(tailPt.x, tailPt.y); ctx.stroke(); } function findHeading() { var dot = (headPt.x-tailPt.x)*1 + (headPt.y-tailPt.y)*0; var line = headPt.sub(tailPt); var len = line.length(); var cosVal = dot/len; var rad = Math.acos(cosVal); var deg = rad * (180/Math.PI); /* Javascript의 수학 라이브러리는 Math.acos()의 경우 그 리턴 값이 0~180 deg 이므로 우측에서 시작하여 180도까지는 실제 각도와 일치하지만 180도를 넘어선 각도는 원점에서 반시계 방향으로 증가하는 수로 리턴하는 특징을 가진다. 그러므로 어떤 벡터가 가리키는 방향이 위쪽이라면 아래와 같은 방법을 이용하여 360도 체제로 변환할 필요가 있다 */ if(line.y<0) {//타겟이 위쪽에 있는 경우에는 각도를 보정해야 한다. //deg = 180-deg+180; rad = Math.PI - rad + Math.PI; deg = rad * 180/Math.PI } dirRad = rad; } function setHeading() { var cosVal = Math.cos(dirRad); var sinVal = Math.sin(dirRad); speedX = cosVal; speedY = sinVal; } function gameLoop() { ctx.fillStyle = "white"; ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); ctx.fill(); ctx.fillStyle = "black"; displayHeading(); drawCross(); crossPt.x += speedX*5; crossPt.y += speedY*5; drawLine(); } function drawCross(){ ctx.beginPath(); ctx.strokeStyle = "black"; ctx.moveTo(crossPt.x-20, crossPt.y); ctx.lineTo(crossPt.x+20, crossPt.y); ctx.moveTo(crossPt.x, crossPt.y-20); ctx.lineTo(crossPt.x, crossPt.y+20); ctx.stroke(); } function displayHeading() { ctx.fillStyle = "black"; ctx.font = "bold 20px sans-serif"; ctx.fillText("Heading:"+Math.round(dirRad*DEG),100,20); } </script> </head> <body> <div align="center"> <canvas id="canvas1" width="400" height="300"></canvas> </div> </body> </html>