Thứ hai, ngày 5 tháng 12 năm 2016

HTML5 – Canvas: Di chuyển đối tượng bằng bàn phím

Ngày đăng: 18/4/2012, 8:26:52AM | Lượt xem: 5,502
Hot!

Bàn phím là thiết bị không thể thiếu và là phương tiện rất quan trọng để thực hiện các chức năng của các ứng dụng tương tác với người dùng. Trong bài viết này, tôi sẽ hướng dẫn cách bắt sự kiện bàn phím trong canvas và dùng nó để điều khiển góc xoay và hướng di chuyển của đối tượng đồ họa.

preferences-desktop-keyboard-shortcutsBàn phím là thiết bị không thể thiếu và là phương tiện rất quan trọng để thực hiện các chức năng của các ứng dụng tương tác với người dùng. Trong bài viết này, tôi sẽ hướng dẫn cách bắt sự kiện bàn phím trong canvas và dùng nó để điều khiển góc xoay và hướng di chuyển của đối tượng đồ họa.

Xem Demo.

Bắt sự kiện bàn phím

Để bắt sự kiện này, bạn có thể đăng kí trực tiếp cho đối tượng window, tuy nhiên điều này sẽ gây ra những rắc rối khi trang của bạn có quá nhiều thành phần. Cách tốt nhất là đăng kí riêng cho canvas các hàm xử lý. Tuy nhiên, để canvas có thể focus được, bạn cần xác định thuộc tính TabIndex của nó:

<canvas id=”canvas” width=”300″ height=”200″

tabindex=”1″ style=”border: 1px solid;”></canvas>

Sau đó bạn đăng kí các sự kiện cần thiết cho canvas. Khi chạy trên trình duyệt, bạn cần phải click chuột vào canvas để nó nhận được focus trước khi thực hiện các thao tác bàn phím.

_canvas.onkeydown = canvas_keyDown;

function canvas_keyDown(e){
	alert(e.keyCode);
}

Tham số event truyền vào hàm xử lý sẽ chứa các thuộc tính cần thiết để bạn xác định được phím nào được nhấn. Ở đây, bạn chỉ cần quan tâm đến thuộc tính keyCode lưu mã của phím được nhấn. Do các giá trị keyCode có kiểu số nên rất khó nhớ và kiểm tra, may mắn là tôi tìm được trang “Javascript Keycode Enums” liệt kê sẵn các keyCode dưới dạng enum của một đối tượng. Tôi sửa lại một chút để tiện sử dụng hơn:

var Keys = {
      BACKSPACE: 8,
      TAB: 9,
      ENTER: 13,
      SHIFT: 16,
      CTRL: 17,
      ALT: 18,
      PAUSE: 19,
      CAPS_LOCK: 20,
      ESCAPE: 27,
      SPACE: 32,
      PAGE_UP: 33,
      PAGE_DOWN: 34,
      END: 35,
      HOME: 36,
      LEFT_ARROW: 37,
      UP_ARROW: 38,
      RIGHT_ARROW: 39,
      DOWN_ARROW: 40,
      INSERT: 45,
      DELETE: 46,
      KEY_0: 48,
      KEY_1: 49,
      KEY_2: 50,
      KEY_3: 51,
      KEY_4: 52,
      KEY_5: 53,
      KEY_6: 54,
      KEY_7: 55,
      KEY_8: 56,
      KEY_9: 57,
      KEY_A: 65,
      KEY_B: 66,
      KEY_C: 67,
      KEY_D: 68,
      KEY_E: 69,
      KEY_F: 70,
      KEY_G: 71,
      KEY_H: 72,
      KEY_I: 73,
      KEY_J: 74,
      KEY_K: 75,
      KEY_L: 76,
      KEY_M: 77,
      KEY_N: 78,
      KEY_O: 79,
      KEY_P: 80,
      KEY_Q: 81,
      KEY_R: 82,
      KEY_S: 83,
      KEY_T: 84,
      KEY_U: 85,
      KEY_V: 86,
      KEY_W: 87,
      KEY_X: 88,
      KEY_Y: 89,
      KEY_Z: 90,
      LEFT_META: 91,
      RIGHT_META: 92,
      SELECT: 93,
      NUMPAD_0: 96,
      NUMPAD_1: 97,
      NUMPAD_2: 98,
      NUMPAD_3: 99,
      NUMPAD_4: 100,
      NUMPAD_5: 101,
      NUMPAD_6: 102,
      NUMPAD_7: 103,
      NUMPAD_8: 104,
      NUMPAD_9: 105,
      MULTIPLY: 106,
      ADD: 107,
      SUBTRACT: 109,
      DECIMAL: 110,
      DIVIDE: 111,
      F1: 112,
      F2: 113,
      F3: 114,
      F4: 115,
      F5: 116,
      F6: 117,
      F7: 118,
      F8: 119,
      F9: 120,
      F10: 121,
      F11: 122,
      F12: 123,
      NUM_LOCK: 144,
      SCROLL_LOCK: 145,
      SEMICOLON: 186,
      EQUALS: 187,
      COMMA: 188,
      DASH: 189,
      PERIOD: 190,
      FORWARD_SLASH: 191,
      GRAVE_ACCENT: 192,
      OPEN_BRACKET: 219,
      BACK_SLASH: 220,
      CLOSE_BRACKET: 221,
      SINGLE_QUOTE: 222
    };

Kiểm tra trạng thái của nhiều phím

Một khó khăn của các sự kiện bàn phím trong javascript là chỉ có thể xác định được duy nhất một phím nhấn thông qua thuộc tính event.keyCode. Để có thể kiểm tra được trạng thái của nhiều phím được nhấn cùng lúc, ta phải lưu trạng thái của chúng lại khi sự kiện keyDown xảy ra và  xóa trạng thái đó đi trong sự kiện keyUp.

var _keypressed = {};
function canvas_keyDown(e){
	_keypressed[e.keyCode] = true;
}
function canvas_keyUp(e){
	_keypressed[e.keyCode] = false;
}

Giới hạn các phím được bắt

Sử dụng phương pháp trên, bạn cần lọc các phím cần sử dụng để đối tượng _keypressed không lưu các giá trị không cần thiết. Một cách đơn giản nhất là sử dụng mảng để lưu keyCode của các phím này và kiểm tra trong các sự kiện bàn phím:

const AVAILABLE_KEYS =
	[ 	Keys.UP_ARROW,
		Keys.DOWN_ARROW,
		Keys.LEFT_ARROW,
		Keys.RIGHT_ARROW
	];
function canvas_keyDown(e){
	if(AVAILABLE_KEYS.indexOf(e.keyCode)!=-1)
	{
		_keypressed[e.keyCode] = true;
	}
}
function canvas_keyUp(e){
	if(_keypressed[e.keyCode])
	{
		_keypressed[e.keyCode] = false;
	}
}

Ví dụ: Di chuyển xe tăng

Trong ví dụ này tôi sẽ tạo một lớp Tank và vẽ theo hình dạng một chiếc xe tăng đơn giản. Tại trang html, tôi sẽ bắt các sự kiện keyDown và keyUp để di chuyển đối tượng này theo 8 tám hướng

Xem Demo.

HTML5 - canvas - keyboard event

function Tank(mapWidth, mapHeight){
	this.mapWidth = mapWidth;
	this.mapHeight = mapHeight;
	this.radius = 20;
	this.speedX = 0;
	this.speedY = 0;
	this.power = 3;
	this.cx = mapWidth/2;
	this.cy = mapHeight/2;
}
Tank.prototype.draw = function(context){
	context.beginPath();
	context.fillStyle = "green";
	context.arc(this.cx,this.cy,this.radius,0,Math.PI*2,true);
	context.closePath();
	context.fill();

	// gun
	var size = this.radius/2;
	var angle = Math.atan2(this.speedY,this.speedX);;

	context.beginPath();
	context.fillStyle = "red";
	context.rect(this.cx,this.cy - size/2,size*3,size);
	context.closePath();
	context.fill();

	// yellow circle
	context.beginPath();
	context.fillStyle = "yellow";
	context.arc(this.cx,this.cy,this.radius/2,0,Math.PI*2,true);
	context.closePath();
	context.fill();

}

Tank.prototype.move = function(){
	this.cx += this.speedX;
	this.cy += this.speedY;

	this.left = this.cx - this.radius;
	this.top = this.cy - this.radius;
	this.right = this.cx + this.radius;
	this.bottom = this.cy + this.radius;
}
Tank.prototype.resetSpeed = function(){
	this.speedX = 0;
	this.speedY = 0;
}
Tank.prototype.moveUp = function(){
	this.speedY = -this.power;
}
Tank.prototype.moveDown = function(){
	this.speedY = this.power;
}
Tank.prototype.moveLeft = function(){
	this.speedX = -this.power;
}
Tank.prototype.moveRight = function(){
	this.speedX = this.power;
}

Xoay đối tượng trong canvas

Một vấn đề khi bạn di chuyển xe tăng là súng không thay đổi hướng và góc xoay cho phù hợp. Nhưng điều này giải quyết rất đơn giản, bởi vì đối tượng context của canvas hỗ trợ phương thức translate()rotate() dùng để dịch chuyển và xoay canvas. Như vậy, tôi sẽ thực hiện dịch chuyển tâm xoay đến giữa đối tượng (xe tăng) và gọi rotate() trước khi vẽ nòng súng ra canvas.

Để lấy được góc xoay, ta biết được hai giá trị speedX và speedY chính là hướng di chuyển của đối tượng. Hai giá trị này tạo thành hai cạnh kề của một tam giác vuông và góc của súng cũng chính là góc α được tính bằng công thức tan(α) = speedY/speedX.

Tan Angle

Như vậy ta có:

tan(α) = speedY/speedX.

=>  α = atan(speedY/speedX)

hoặc atan2(speedY,speedX)

Như vậy ta sẽ vẽ súng tại điểm gốc tọa độ là chính giữa xe tăng. Bạn nên lưu lại trạng thái của context với phương thức save() và gọi restore() để phục hồi lại trạng thái ban đầu cho nó khi thực hiện thay đổi các thông số của nó.

var angle = Math.atan2(this.speedY,this.speedX)
context.save();
context.translate(this.cx,this.cy);
context.rotate(angle);
context.beginPath();
context.fillStyle = "red";
context.rect(0,-size/2,size*3,size);
context.closePath();
context.fill();
context.restore();

Xem Demo.

 HTML5 - canvas - rotate shape

Mã nguồn hoàn chỉnh

Tank.js:

function Tank(mapWidth, mapHeight){
	this.mapWidth = mapWidth;
	this.mapHeight = mapHeight;
	this.radius = 20;
	this.speedX = 0;
	this.speedY = 0;
	this.power = 3;
	this.cx = mapWidth/2;
	this.cy = mapHeight/2;
}
Tank.prototype.draw = function(context){
	context.beginPath();
	context.fillStyle = "green";
	context.arc(this.cx,this.cy,this.radius,0,Math.PI*2,true);
	context.closePath();
	context.fill();

	// red rectangle
	var size = this.radius/2;
	var angle = Math.atan2(this.speedY,this.speedX);

	context.save();
	context.translate(this.cx,this.cy);
	context.rotate(angle);
	context.beginPath();
	context.fillStyle = "red";
	context.rect(0,-size/2,size*3,size);
	context.closePath();
	context.fill();
	context.restore();

	// yellow circle
	context.beginPath();
	context.fillStyle = "yellow";
	context.arc(this.cx,this.cy,this.radius/2,0,Math.PI*2,true);
	context.closePath();
	context.fill();

}

Tank.prototype.move = function(){
	this.cx += this.speedX;
	this.cy += this.speedY;

	this.left = this.cx - this.radius;
	this.top = this.cy - this.radius;
	this.right = this.cx + this.radius;
	this.bottom = this.cy + this.radius;
}
Tank.prototype.resetSpeed = function(){
	this.speedX = 0;
	this.speedY = 0;
}
Tank.prototype.moveUp = function(){
	this.speedY = -this.power;
}
Tank.prototype.moveDown = function(){
	this.speedY = this.power;
}
Tank.prototype.moveLeft = function(){
	this.speedX = -this.power;
}
Tank.prototype.moveRight = function(){
	this.speedX = this.power;
}

Sample.html:

<html>
<head>
<script src="tank.js"></script>
<script src="keys.js"></script>
<script type="text/javascript">
const AVAILABLE_KEYS =
	[ 	Keys.UP_ARROW,
		Keys.DOWN_ARROW,
		Keys.LEFT_ARROW,
		Keys.RIGHT_ARROW
	];

var _canvas;
var _context;
var _ball;
var _keypressed = {};

function clear() {
	_context.clearRect(0, 0, _canvas.width, _canvas.height);
}

function init() {
	_canvas = document.getElementById("canvas");
	_context = _canvas.getContext("2d");
	_ball = new Tank(_canvas.width,_canvas.height);
	draw();
}

function canvas_keyDown(e){
	if(AVAILABLE_KEYS.indexOf(e.keyCode)!=-1)
	{
		_keypressed[e.keyCode] = true;
		doKeypress();
	}
}
function canvas_keyUp(e){
	if(_keypressed[e.keyCode])
	{
		_keypressed[e.keyCode] = false;
		_ball.resetSpeed();
	}
}

function doKeypress(){
	if(_keypressed[Keys.UP_ARROW])
		_ball.moveUp();
	if(_keypressed[Keys.DOWN_ARROW])
		_ball.moveDown();
	if(_keypressed[Keys.LEFT_ARROW])
		_ball.moveLeft();
	if(_keypressed[Keys.RIGHT_ARROW])
		_ball.moveRight();
	_ball.move();
	draw();
}
function draw() {
	clear();
	_ball.draw(_context);
}
window.onload = function(){
	init();
_canvas.onkeydown = canvas_keyDown;
_canvas.onkeyup = canvas_keyUp;
}
</script>
</head>
<body>

<canvas id="canvas" width="300" height="200"
	tabindex="1" style="border: 1px solid;"></canvas>
</body>
</html>

YinYang’s Programming Blog

 Chia sẻ qua: 
Hot!
Ý kiến bạn đọc

These items will be permanently deleted and cannot be recovered. Are you sure?

Gallery

image

Maecenas viverra rutrum pulvinar

Maecenas viverra rutrum pulvinar! Aenean vehicula nulla sit amet metus aliquam et malesuada risus aliquet. Vestibulum rhoncus, dolor sit amet venenatis porta, metus purus sagittis nisl, sodales volutpat elit lorem…

Read more

Text Links

Thiết kế logo chuyên nghiệp Insky
DAFABET
W88 w88b.com/dang-ky-tai-khoan-w88
W88
Copyright © 2011 - 2012 vietshare.vn by phamkhuong102@gmail.com doanhkisi2315@gmail.com. All rights reserved.