HTML5: Chuyển động trong Canvas
Một ví dụ đơn giản để khi làm quen với đồ họa và chuyển động trong lập trình là viết một ví dụ bóng nảy bên trong một vùng cửa sổ (canvas). Một quả bóng sẽ được vẽ bên trong canvas và chuyển động theo một hướng xác định. Khi chạm bất kì thành tường nào, bóng sẽ đổi hướng chuyển động tùy theo hướng di chuyển.
1. Cơ bản
Ta xác định hai giá trị speedX và speedY tương ứng với tốc độ di chuyển theo phương ngang và dọc của trái bóng. Hướng di chuyển của bóng phụ thuộc vào giá trị âm hoặc dương của speedX và speedY.
Trong ví dụ này, thành tường là các biên của canvas và chỉ có hai phương là ngang và dọc.
Nguyên lý hoạt động rất đơn giản: khi bóng tiếp xúc với biên dọc của canvas, ta sẽ đổi chiều của speedY và với biên ngang ta sẽ đổi chiều của speedX.
Trong lớp Ball sau, ta sẽ bổ sung thêm các thuộc tính right, bottom để tiện khi kiểm tra va chạm.
Hai thuộc tính cx và cy là vị trí tâm của bóng và sẽ được khởi tạo ngẫu nhiên sao cho nó luôn nằm hoàn toàn bên trong canvas.
Tập tin Ball.js:
function Ball(mapWidth, mapHeight){
this.mapWidth = mapWidth;
this.mapHeight = mapHeight;
this.radius = 20;
this.speedX = 3;
this.speedY = 3;
this.cx = Math.floor(Math.random()*(this.mapWidth-2*this.radius)) + this.radius;
this.cy = Math.floor(Math.random()*(this.mapHeight-2*this.radius)) + this.radius;
}
Ball.prototype.draw = function(context){
context.beginPath();
context.fillStyle = "red";
context.arc(this.cx,this.cy,this.radius,0,Math.PI*2,true);
context.closePath();
context.fill();
}
Ball.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;
}
Ball.prototype.checkCollision = function(shapes) {
if(this.left <= 0 || this.right >= this.mapWidth)
this.speedX = -this.speedX;
if(this.top <= 0 || this.bottom >= this.mapHeight)
this.speedY = -this.speedY;
}
Sample.HTML:
<HTML>
<head>
<script src="ball.js"></script>
<script>
var _canvas;
var _context;
var _ball;
function draw(){
_context.clearRect(0,0,_canvas.width,_canvas.height);
_ball.draw(_context);
}
function update(){
_ball.move();
_ball.checkCollision();
draw();
}
window.onload = function(){
var interval = 10;
_canvas = document.getElementById("canvas");
_context = _canvas.getContext("2d");
_ball = new Ball(_canvas.width,_canvas.height);
setInterval("update()",interval);
}
</script>
</head>
<body>
<canvas id="canvas" width="400px" height="300px" style="border: 1px solid gray;"></canvas>
</body>
</HTML>
2. Thêm hiệu ứng bóng di chuyển
Để ví dụ không quá đơn điệu, ta sẽ thêm các ảnh bóng mờ di chuyển phía sau trái bóng chính. Ta sẽ lưu các vị trí bóng di chuyển qua vào một mảng có tối đa 10 phần tử và vẽ ra canvas trong hàm draw(). Phương thức Ball.draw() cần chỉnh sửa một chút để cho phép nhận tham số alpha xác định độ mờ đục. Tham số alpha này có giá trị nằm giữa đoạn 0 và 1:
Ball.prototype.draw = function(context,alpha){
if(!alpha)
alpha = 255;
context.beginPath();
context.fillStyle = "rgba(255, 100, 100," + alpha + ")";
context.arc(this.cx,this.cy,this.radius,0,Math.PI*2,true);
context.closePath();
context.fill();
}
Để các trái bóng không nằm quá sát nhau, ta chỉ thực hiện lưu vị trí của bóng sau mỗi 5 lần bóng di chuyển hay sau mỗi 5 lần phương thức update() được gọi. Phương thức traceBall() sau sẽ tạo ra một đối tượng Ball mới với hai giá trị cx và cy lấy từ trái bóng chính và lưu vào mảng _balls.
Khi chiều dài của mảng _balls vượt quá 10, ta dùng phương thức Array.splice() để cắt đi phần tử nằm ở đầu mảng:
function traceBall(ball){
var b = new Ball;
b.cx = ball.cx;
b.cy = ball.cy;
_balls.push(b);
if(_balls.length>10)
_balls.splice(0,1);
}
Kết quả:
3. Kiểm tra va chạm
Phần quan trọng của ví dụ này nằm ở phương thức kiểm tra va chạm của trái bóng. Phương thức này sẽ duyệt qua tất cả các chướng ngại vật và kiểm tra khoảng cách của nó so với chướng ngại vật theo hai phương dọc và ngang.
Với ví dụ này, ta kiểm tra va chạm ở mức rất đơn giản nên việc phản xạ của trái bóng chưa hoàn toàn chính xác. Ta sẽ giới thiệu các kĩ thuật kiểm tra va chạm và di chuyển trong các bài viết sau.
Phương thức kiểm tra va chạm của lớp Ball:
Ball.prototype.checkCollision = function(shapes) {
if(this.left <= 0 || this.right >= this.mapWidth)
this.speedX = -this.speedX;
if(this.top <= 0 || this.bottom >= this.mapHeight)
this.speedY = -this.speedY;
for(var i = 0; i < shapes.items.length ; i++){
var item = shapes.items[i];
var hCollision = false;
var vCollision = false;
if( this.right >= item.left && this.left <= item.right && // the ball is
((this.cy < item.top && this.bottom >= item.top) || // on or
(this.cy > item.bottom && this.top <= item.bottom)))
// under the rectangle
{
this.speedY = -this.speedY;
vCollision = true;
}
if(this.bottom >= item.top && this.top <= item.bottom &&
// the ball is in the
((this.cx < item.left && this.right >= item.left) ||
// left or
(this.cx > item.right && this.left <= item.right))) // right side of the rectangle
{
this.speedX = -this.speedX;
hCollision = true;
}
if(hCollision || vCollision)
break;
}
}
Minh họa: