text:
Giới thiệu
Các nhà phát triển đã từng làm việc với Flash hay Silverlight thường ngạc nhiên rằng các ứng dụng được viết cho HTML5 Canvas không cung cấp tiện nghi đặc biệt nào về xử lý dữ liệu đầu vào của người dùng. Về cơ bản, đầu vào từ người dùng của HTML liên quan đến việc sử dụng một hệ thống xử lý sự kiện được xây dựng trong các trình duyệt kể từ những ngày đầu tiên mà trình duyệt hỗ trợ JavaScript; không có gì đặc trưng cho HTML5 để phát hiện và xử lý dữ liệu đầu vào từ người dùng. Ví dụ như khả năng cung cấp thông tin phản hồi mức thấp để chỉ ra tọa độ (x, y) mà người dùng đã nhấn chuột vào.
Việc xử lý tương tác của người dùng không có gì khác so với những kiến trúc trò chơi khác. Không có những sự trừu tượng hóa dựng sẵn nào để thông báo cho bạn khi người dùng đã tương tác với một đối tượng cụ thể được biểu thị trên Canvas. Điều này tạo ra một phạm vi rất lớn về kiểm soát mức thấp về cách bạn muốn xử lý các sự kiện đó ra sao. Miễn là bạn có thể không chọn hoặc chọn sai khai báo kiểu tài liệu (DOCTYPE) của trình duyệt khác nhau, thì cuối cùng bạn có thể điều chỉnh việc xử lý các sự kiện để đạt được hiệu quả tối đa theo một ứng dụng duy nhất—chứ không bị gắn chặt vào một cách triển khai thực hiện cụ thể.
Trong bài này, hãy tìm hiểu các kỹ thuật để xử lý tương tác người dùng trong các trò chơi dựa trên HTML Canvas. Các ví dụ minh họa cách xử lý các sự kiện bàn phím, chuột và cảm ứng chạm. Các chiến lược để gửi sự kiện đến các đối tượng trò chơi và khả năng tương thích với thiết bị di động cũng được trình bày trong bài này.
Bạn có thể tải về mã nguồn cho các ví dụ.
Các loại sự kiện
Sự tương tác của người dùng được xử lý hoàn toàn bằng mô hình lắng nghe sự kiện truyền thống của trình duyệt. Không có gì mới khi HTML5 phát hành; đó vẫn là mô hình sự kiện tương tự như đã được sử dụng từ những ngày đầu của Netscape Navigator.
Về cơ bản, hãy suy nghĩ về một ứng dụng hoặc trò chơi tương tác như là một gắn kết giữa mô hình sự kiện trình duyệt dành cho dữ liệu đầu vào của người dùng và Canvas dành cho đồ họa đầu ra. Không có sự kết nối logic nào giữa hai thứ đó trừ khi bạn tự mình xây dựng nó.
Bạn sẽ tận dụng được lợi thế là những trình lắng nghe sự kiện có thể được
gắn kèm với chính phần tử
<canvas>. Vì phần tử
<canvas> chỉ đơn giản là một phần tử mức-khối
(block-level), về phần trình duyệt điều này không có gì khác hơn việc gắn
kèm các trình nghe sự kiện vào một
<div> hoặc bất kỳ
phần tử mức-khối nào khác.
Các sự kiện bàn phím
Các loại sự kiện đơn giản nhất để nghe và xử lý là các sự kiện bàn phím. Chúng không phụ thuộc vào phần tử Canvas hoặc vị trí con trỏ của người dùng. Các sự kiện bàn phím chỉ yêu cầu bạn nghe các sự kiện phím xuống, phím lên và nhấn phím ở mức tài liệu.
Nghe các sự kiện bàn phím
Mô hình trình nghe sự kiện có thể khác nhau tùy thuộc vào việc thực hiện của trình duyệt, do đó, cách nhanh nhất để dựng lên và chạy là sử dụng một thư viện để chuẩn hóa việc xử lý các sự kiện. Các ví dụ sau sử dụng jQuery để kết buộc các sự kiện. Đây thường là cách dễ nhất để bắt đầu, nhưng để tương thích với các trình duyệt cũ, hiệu năng có thể bị ảnh hưởng do mức mã dư thừa hoặc mã viết kém có liên quan đến nỗ lực của jQuery. Một thư viện phổ biến khác, được viết riêng để xử lý sự kiện bàn phím nhanh chóng giữa các trình duyệt, là Kibo (xem phần Tài nguyên).
Liệt kê 1 minh họa việc nghe các sự kiện nhấn phím và có hành động thích hợp dựa trên việc nhấn phím nào.
Liệt kê 1. Xử lý các sự kiện bàn phím
$(document.body).on('keydown', function(e) { switch (e.which) { // key code for left arrow case 37: console.log('left arrow key pressed!'); break; // key code for right arrow case 39: console.log('right arrow key pressed!'); break; } });
Nếu ứng dụng của bạn diễn ra trong môi trường của một trình duyệt web, điều quan trọng là luôn để tâm đến các tổ hợp bàn phím nhạy cảm. Trong khi, về kỹ thuật, có thể định nghĩa các hành vi đối với các tổ hợp phím phổ biến nào đó sẽ ghi đè lên các hành vi trình duyệt mặc định của chúng (chẳng hạn như tổ hợp phím CTRL-R), bạn cần tránh điều này.
Các sự kiện chuột
Các sự kiện chuột phức tạp hơn nhiều so với các sự kiện bàn phím. Bạn phải nhận biết về vị trí của phần tử Canvas bên trong cửa sổ trình duyệt cũng như vị trí con trỏ của người dùng.
Lắng nghe các sự kiện chuột
Thật dễ dàng để có được vị trí của chuột tương đối so với toàn bộ cửa sổ
trình duyệt bằng cách sử dụng các thuộc tính
e.pageX và
e.pageY. Trong trường hợp này, gốc (0,0) sẽ được đặt ở góc
trên cùng bên trái của toàn bộ cửa sổ trình duyệt.
Bạn thường không quan tâm quá nhiều về dữ liệu đầu vào của người dùng khi con trỏ của người dùng không đặt trong vùng Canvas. Vì vậy, sẽ tốt hơn nếu coi gốc (0,0) được đặt ở góc trên bên trái của phần tử Canvas. Lý tưởng nhất là bạn cần hoạt động bên trong hệ tọa độ cục bộ tương đối so với vùng Canvas chứ không phải là một hệ thống tọa độ chung (global) liên quan đến toàn bộ cửa sổ trình duyệt.
Các chiến lược về sự kiện chuột
Sử dụng các bước sau đây để chuyển đổi các tọa độ cửa sổ chung sang các tọa độ Canvas cục bộ.
- Tính toán vị trí (x, y) của phần tử DOM của Canvas trên trang web.
- Xác định vị trí chung của chuột liên quan đến toàn bộ tài liệu.
- Để xác định vị trí gốc (0,0) ở góc trên bên trái của phần tử Canvas và chuyển đổi hiệu quả các tọa độ chung thành các tọa độ tương đối, hãy lấy hiệu số giữa vị trí chuột chung được tính ở bước 2 và vị trí Canvas được tính ở bước 1.
Hình 1 cho thấy một ví dụ về thông tin mà bạn cần nắm bắt về mặt hệ tọa độ chung của trang web.
Hình 1. Vị trí chuột, các tọa độ chung
Hình 2 cho thấy kết quả sau khi chuyển đổi vị trí chuột sang các tọa độ cục bộ.
Hình 2. Vị trí chuột sau khi chuyển đổi sang các tọa độ cục bộ
Liệt kê 2 cho thấy phương thức xác định các tọa độ chuột cục bộ. Giả định
rằng bạn đã định nghĩa một phần tử Canvas theo cách đánh dấu, như sau:
<canvas id="my_canvas"></canvas>.
Liệt kê 2. Xử lý các sự kiện chuột
var canvas = $('#my_canvas'); // calculate position of the canvas DOM element on the page var canvasPosition = { x: canvas.offset().left, y: canvas.offset().top }; canvas.on('click', function(e) { // use pageX and pageY to get the mouse position // relative to the browser window var mouse = { x: e.pageX - canvasPosition.x, y: e.pageY - canvasPosition.y } // now you have local coordinates, // which consider a (0,0) origin at the // top-left of canvas element });
Các hành vi trình duyệt không mong muốn
Trong một trò chơi máy tính, bạn thường không muốn bất kỳ hành vi trình duyệt mặc định nào can thiệp vào các hành động của mình. Ví dụ, bạn không muốn kéo chuột để thực hiện chọn văn bản, một lần nhấn phím chuột phải để mở trình đơn ngữ cảnh hoặc cuộn bánh xe chuột để di chuyển lên và xuống trang.
Hình 3 cho thấy một ví dụ về những gì có thể xảy ra nếu người dùng nhấn phím chuột và kéo một hình ảnh trong trình duyệt. Mặc dù hành vi trình duyệt mặc định hoàn toàn có nghĩa đối với các ứng dụng kéo và thả, những nó không phải là một hành vi mà bạn muốn có trong trò chơi của mình.
Hình 3. Hành vi trình duyệt mặc định khi kéo một hình ảnh
Trong tất cả các trình xử lý sự kiện, hãy thêm một dòng
preventDefault() và trả về false (sai) từ hàm đó. Mã trong
Liệt kê 3 sẽ thực hiện thủ thuật theo cách ngăn chặn cả hành động mặc định
lẫn sự kiện xảy ra.
Liệt kê 3. Ngăn chặn hành vi mặc định
canvas.on('click', function(e) { e.preventDefault(); var mouse = { x: e.pageX - canvasPosition.x, y: e.pageY - canvasPosition.y } //do something with mouse position here return false; });
Ngay cả với mã trong Liệt kê 3, bạn vẫn có thể gặp phải một số tác dụng phụ không mong muốn khi người dùng bắt đầu một sự kiện kéo trên một phần tử DOM, chẳng hạn như sự xuất hiện của con trỏ dạng I (I-beam), sự lựa chọn văn bản và v.v. Theo truyền thống, vấn đề sự kiện kéo phổ biến hơn với các hình ảnh, nhưng để áp dụng nó cho phần tử Canvas để ngăn chặn việc kéo và chọn là một ý tưởng tốt. Liệt kê 4 cho thấy một quy tắc CSS để ngăn chặn các tác dụng phụ của thao tác chọn bằng cách thêm một chút CSS.
Liệt kê 4. Các kiểu dáng nên dùng để ngăn chặn thao tác chọn
image, canvas { user-select: none; -ms-user-select: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -webkit-touch-callout: none; -webkit-user-drag: none; }
Ghi đè lên các hành vi máy tính để bàn
Nói chung, ghi đè lên các sự kiện kéo và chọn là một ý tưởng tốt để đảm bảo rằng hành vi kéo và chọn mặc định của trình duyệt không ngóc đầu dậy.
Mã trong Liệt kê 5 cố ý không sử dụng jQuery để gắn kèm các sự kiện. jQuery
không xử lý đúng đắn các sự kiện
ondragstart và
onselectstart (nếu được gắn kèm bằng cách sử dụng jQuery, thì
các trình xử lý sự kiện có thể không bao giờ bắt đầu được).
Liệt kê 5. Hủy bỏ các sự kiện kéo và chọn
var canvasElement = document.getElementById('my_canvas'); // do nothing in the event handler except canceling the event canvasElement.ondragstart = function(e) { if (e && e.preventDefault) { e.preventDefault(); } if (e && e.stopPropagation) { e.stopPropagation(); } return false; } // do nothing in the event handler except canceling the event canvasElement.onselectstart = function(e) { if (e && e.preventDefault) { e.preventDefault(); } if (e && e.stopPropagation) { e.stopPropagation(); } return false; }
Ghi đè lên các hành vi trên thiết bị di động
Trên các thiết bị di động, điều rất quan trọng là bạn ngăn chặn người dùng phóng đại và mở rộng cửa sổ trình duyệt (việc phóng đại và mở rộng thường là hành vi mặc định của trình duyệt cho thiết bị di động đối với các cử chỉ cảm ứng chạm).
Bạn có thể ngăn chặn hành vi phóng đại bằng cách thêm
user-scalable=no vào siêu thẻ
viewport. Ví
dụ:
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1" />
Để vô hiệu hóa tất cả các chuyển động của tài liệu hoặc cửa sổ khi có các cử chỉ chạm, hãy đính kèm các trình nghe sự kiện trong Liệt kê 6 vào tệp document.body. Về cơ bản, điều này sẽ hủy bỏ tất cả các hành vi trình duyệt mặc định nếu người dùng tình cờ chạm nhẹ vào bất cứ chỗ nào bên ngoài vùng Canvas hoặc vùng trò chơi.
Liệt kê 6. Hủy chuyển động cửa sổ trên thiết bị di động
document.body.ontouchstart = function(e) { if (e && e.preventDefault) { e.preventDefault(); } if (e && e.stopPropagation) { e.stopPropagation(); } return false; } document.body.ontouchmove = function(e) { if (e && e.preventDefault) { e.preventDefault(); } if (e && e.stopPropagation) { e.stopPropagation(); } return false; }
Phát quảng bá tới các đối tượng trò chơi
Bạn cần đính kèm chỉ một trình nghe sự kiện vào Canvas cho mỗi kiểu sự kiện mà bạn muốn bắt giữ. Ví dụ, nếu bạn cần bắt giữ các sự kiện nhấn chuột và di chuyển chuột (mousemove), thì chỉ cần đính kèm một trình nghe sự kiện nhấn chuột duy nhất và một trình nghe sự kiện di chuyển chuột duy nhất vào Canvas. Các trình nghe sự kiện chỉ cần được đính kèm một lần, vì thế, thông thường là đính kèm các sự kiện này trong lúc khởi tạo ứng dụng.
Nếu bạn cần các trình nghe sự kiện bắt giữ bất kỳ thông tin có ích nào lan truyền xuống tới các đối tượng được biểu hiện trên Canvas, bạn phải xây dựng logic riêng của mình cho hệ thống. Trong ví dụ này, một hệ thống như vậy sẽ chịu trách nhiệm phát quảng bá sự kiện nhấn chuột hoặc di chuyển chuột tới tất cả các đối tượng trò chơi liên quan đến việc xử lý một trong những sự kiện đó.
Khi mỗi đối tượng trò chơi học một trong những sự kiện này, đầu tiên đối tượng trò chơi sẽ cần nhận biết xem sự kiện nhấn chuột hoặc di chuyển chuột có liên quan đến chúng không. Nếu có, thì đối tượng trò chơi đó cần xác định xem các tọa độ chuột có vị trí ở bên trong các ranh giới riêng của nó hay không.
Các chiến lược phát quảng bá
Chiến lược chính xác của bạn sẽ khác nhau dựa trên các kiểu trò chơi. Ví dụ, một bộ các hình ảnh nhỏ hơn dùng trong 2D (2D tileset) có thể có một chiến lược khác so với một thế giới 3D.
Các bước sau đây phác thảo một cách triển khai thực hiện đơn giản có thể làm việc tốt với một ứng dụng 2D đơn giản.
- Phát hiện các tọa độ nhấn chuột của người dùng trong vùng Canvas.
- Thông báo cho tất cả các đối tượng trò chơi rằng một sự kiện nhấn chuột đã xảy ra tại tập hợp các tọa độ cụ thể.
- Đối với mỗi đối tượng trò chơi, thực hiện phép kiểm tra nhấn chuột giữa các tọa độ chuột và hộp giới hạn chứa đối tượng trò chơi để xác định xem các tọa độ chuột có va chạm với đối tượng đó không.
Ví dụ phát quảng bá đơn giản
Trình xử lý sự kiện nhấn chuột có thể trông giống như Liệt kê 7. Ví dụ này
giả định rằng bạn đã thiết lập một kiểu cấu trúc nào đó để theo dõi tất cả
các đối tượng trò chơi trên thế giới. Vị trí và các chiều của tất cả các
đối tượng trò chơi được lưu trữ trong một biến gọi là
gameObjectArray.
Liệt kê 7. Phát quảng bá trình xử lý sự kiện nhấn chuột cho các đối tượng trò chơi
// initialize an array of game objects // at various positions on the screen using // new gameObject(x, y, width, height) var gameObjectArray = [ new gameObject(0, 0, 200, 200), new gameObject(50, 50, 200, 200), new gameObject(500, 50, 100, 100) ]; canvas.on('click', function(e) { var mouse = { x: e.pageX - canvasPosition.x, y: e.pageY - canvasPosition.y } // iterate through all game objects // and call the onclick handler of each for (var i=0; i < gameObjectArray.length; i++) { gameObjectArray[i].handleClick(mouse); } });
Bước tiếp theo là bảo đảm rằng mỗi đối tượng trò chơi có thể thực hiện kiểm tra nhấn chuột để xác định xem tọa độ chuột có va chạm bên trong khu vực hộp giới hạn của các đối tượng trò chơi không. Hình 4 cho thấy một ví dụ về một lần kiểm tra nhấn chuột không thành công.
Hình 4. Nhấn chuột ngoài vùng giới hạn - kiểm tra nhấn chuột không thành công
Hình 5 cho thấy một kiểm tra nhấn chuột thành công.
Hình 5. Nhấn chuột trong vùng giới hạn - kiểm tra nhấn chuột thành công
Bạn có thể định nghĩa một lớp cho các đối tượng trò chơi, như trong Liệt kê
8. Một phép kiểm tra nhấn chuột được thực hiện theo hàm
onclick(), hàm này kiểm tra sự va chạm giữa hộp giới hạn của
đối tượng đó và các tọa độ chuột được chuyển vào làm một tham số.
Liệt kê 8. Lớp đối tượng trò chơi và kiểm tra nhấn chuột
function gameObject(x, y, width, height) { this.x = x; this.y = y; this.width = width; this.height = height; // mouse parameter holds the mouse coordinates this.handleClick = function(mouse) { // perform hit test between bounding box // and mouse coordinates if (this.x < mouse.x && this.x + this.width > mouse.x && this.y < mouse.y && this.y + this.height > mouse.y) { // hit test succeeded, handle the click event! return true; } // hit test did not succeed return false; } }
Cải thiện hiệu quả việc phát quảng bá
Trong nhiều trường hợp, có thể xây dựng một công cụ hiệu quả hơn. Ví dụ, trong một trò chơi với hàng ngàn đối tượng trò chơi, bạn chắc chắn muốn tránh kiểm tra tất cả đối tượng trò chơi đang ở trong hoạt cảnh cứ mỗi lần bắt đầu một sự kiện.
Ví dụ sau đây sử dụng các sự kiện tùy chỉnh của jQuery để gửi một sự kiện tổng hợp. Sự kiện tổng hợp chỉ được xử lý bởi các đối tượng trò chơi nào đang nghe sự kiện cụ thể. Với ví dụ này:
- Xử lý sự kiện nhấn chuột như trước và thực hiện bất kỳ các phép chuyển đổi cần thiết nào (chẳng hạn như chuyển đổi vị trí con chuột theo các tọa độ cục bộ).
- Gửi một sự kiện tổng hợp có chứa các tọa độ chuột đã chuyển đổi làm một tham số.
- Bất kỳ đối tượng trò chơi nào liên quan đến việc xử lý một sự kiện nhấn chuột sẽ thiết lập một trình nghe để nghe sự kiện tổng hợp đó.
Trình xử lý sự kiện nhấn chuột được sửa đổi để chỉ cần kích hoạt một sự
kiện tùy chỉnh. Có thể đặt bất kỳ tên tùy ý nào cho sự kiện tùy chỉnh.
Trong Liệt kê 9, nó được gọi là
handleClick.
Liệt kê 9. Khởi động một sự kiện tùy chỉnh
canvas.on('click', function(e) { var mouse= { x: e.pageX - canvasPosition.x, y: e.pageY - canvasPosition.y } //fire off synthetic event containing mouse coordinate info $(canvas).trigger('handleClick', [mouse]); });
Như hiển thị trong Liệt kê 10, lớp đối tượng trò chơi cũng được sửa đổi.
Thay vì định nghĩa một hàm
onclick, chỉ cần nghe sự kiện
handleClick. Bất cứ khi nào khởi động sự kiện
handleClick bất kỳ các đối tượng trò chơi nào đang nghe sự
kiện đó sẽ bắt đầu các trình xử lý sự kiện tương ứng của chúng.
Liệt kê 10. Xử lý một sự kiện tùy chỉnh
function gameObject(x, y, width, height) { var self = this; this.x = x; this.y = y; this.width = width; this.height = height; $(canvas).on('handleClick', function(e, mouse) { // perform hit test between bounding box // and mouse coordinates if (self.x < mouse.x && self.x + self.width > mouse.x && self.y < mouse.y && self.y + self.height > mouse.y) { // hit test succeeded, handle the click event! } }); }
Kiểm tra nhấn chuột nâng cao
Điều quan trọng là xem xét những gì sẽ xảy ra khi một số đối tượng trò chơi được xếp chồng lên nhau. Nếu người dùng nhấn chuột vào một điểm, ở đây một số đối tượng trò chơi được xếp chồng lên nhau, bạn sẽ cần xác định cách xử lý các hành vi như thế nào. Ví dụ, bạn thường hy vọng chỉ có trình xử lý sự kiện của đối tượng gần nhất được khởi động còn các đối tượng khác bên dưới nó được bỏ qua.
Để xử lý việc xếp chồng lên nhau như vậy, bạn cần phải biết thứ tự, hoặc độ sâu, của mỗi đối tượng trò chơi nằm trong chồng đó. Canvas không trưng ra bất kỳ biểu diễn logic nào của độ sâu, vì vậy một lần nữa, bạn cần đặt ra các quy tắc và tạo ra logic cần thiết để xử lý tình trạng này.
Để đưa vào khái niệm về độ sâu, việc gán một chỉ mục-z cho tất cả các đối tượng trò chơi để mô tả độ sâu của chúng là cần thiết. Liệt kê 11 cho thấy một ví dụ.
Liệt kê 11. Thêm một chỉ mục-z cho đối tượng Trò chơi
function gameObject(x, y, zIndex, width, height) { var self = this; this.x = x; this.y = y; this.zIndex = zIndex; this.width = width; this.height = height; //... }
Để giúp cho việc kiểm tra độ sâu dễ dàng, bạn cần phải thực hiện việc sắp xếp. Trong Liệt kê 12, cấu trúc ví dụ để lưu trữ các đối tượng trò chơi được sắp xếp sao cho đối tượng trò chơi có chỉ mục-z cao nhất sẽ xuất hiện đầu tiên trong danh sách.
Liệt kê 12. Sắp xếp mảng đối tượng trò chơi
// sort in order such that highest z-index occurs first var sortedGameObjectArray = gameObjectArray.sort(function(gameObject1, gameObject2) { if (gameObject1.zIndex < gameObject2.zIndex) return true; else return false; })
Cuối cùng, trong hàm
click, hãy thay đổi mọi thứ để lặp qua
tất cả các đối tượng trò chơi trong mảng đã sắp xếp.
Ngay sau khi bạn nhận được một kết quả đúng từ việc kiểm tra nhấn chuột của
một đối tượng trò chơi, hãy ngừng ngay lập tức, không cho việc nhấn chuột
tiếp tục lan truyền. Nếu bạn không dừng ngay việc kiểm tra này, như trong
Liệt kê 13, thì hành vi không mong muốn của các đối tượng trò chơi với
việc xử lý sự kiện
click (nhấn chuột) ở độ sâu hơn sẽ vẫn
tiếp tục.
Liệt kê 13. Ngừng ngay việc kiểm tra nhấn chuột thành công
canvas.on('click', function(e) { var mouse = { x: e.pageX - canvasPosition.x, y: e.pageY - canvasPosition.y } for (var i=0; i < sortedGameObjectArray.length; i++) { var hitTest = sortedGameObjectArray[i].onclick(mouse); // stop as soon as one hit test succeeds if (hitTest) { break; // break out of the hit test } } });
Các ranh giới của đối tượng trò chơi không theo quy tắc
Mặc dù để thực hiện kiểm tra nhấn chuột dựa vào các hộp giới hạn hình chữ nhật thường đơn giản nhất và hiệu quả nhất, những trong nhiều trường hợp điều đó là chưa đủ. Nếu đối tượng trò chơi có hình dạng bất thường nhiều hơn, thì để kiểm tra dựa vào một vùng giới hạn hình tam giác hoặc đa giác có thể có ý nghĩa hơn. Trong những trường hợp như vậy, bạn sẽ cần thay logic kiểm tra nhấn chuột trong trình xử lý sự kiện của đối tượng trò chơi bằng hình thức phát hiện nhấn chuột nâng cao. Thông thường, bạn cần tham khảo lĩnh vực của vật lý va chạm trò chơi để biết logic phù hợp.
API Canvas cung cấp một hàm thú vị được gọi
IsPointInPath(),
có thể thực hiện các bài kiểm tra va chạm trong vùng đa giác. Về cơ bản,
IsPointInPath(x, y) cho phép bạn kiểm tra xem điểm (x, y) cụ
thể có nằm bên trong một đường tùy ý (về cơ bản là đường ranh giới đa
giác) hay không. Nó sẽ trả về kết quả là đúng nếu tọa độ (x, y) rơi vào
bên trong đường hiện tại, được định nghĩa trong bối cảnh Canvas.
Sử dụng hàm
isPointInPath()
Hình 6 cho thấy một tình huống, ở đó cần kiểm tra các tọa độ chuột dựa vào một đường không phải là hình chữ nhật. Trong trường hợp này, đó là một đường hình tam giác đơn giản.
Hình 6. Nhấn chuột trong ranh giới của một đường hình tam giác
Đường phủ kín mãu đen được hiển thị trực quan chỉ dùng cho mục đích minh
họa. Vì đường này không phải được hiển thị về vật lý trên màn hình để hàm
IsPointInPath() trả về một kết quả có ích, nên chỉ cần định
nghĩa đường này mà không bao giờ thực hiện một cuộc gọi tới hàm
fill() hay
stroke() để thực sự vẽ ra đường đó.
Liệt kê 14 cho thấy các chi tiết.
Liệt kê 14. Sử dụng hàm
isPointInPath để phát hiện nhấn chuột
$(canvas).on('handleClick', function(e, mouse) { // first, define polygonal bounding area as a path context.save(); context.beginPath(); context.moveTo(0,0); context.lineTo(0,100); context.lineTo(100,100); context.closePath(); // do not actually fill() or stroke() the path because // the path only exists for purposes of hit testing // context.fill(); // perform hit test between irregular bounding area // and mouse coordinates if (context.isPointInPath(mouse.x, mouse.y)) { // hit test succeeded, handle the click event! } context.restore(); });
Mặc dù để viết các thuật toán va chạm của chính bạn thường có hiệu quả hơn
chứ không phải bằng cách sử dụng hàm
IsPointInPath(), nó có
thể là một công cụ tốt để tạo nguyên mẫu và phát triển nhanh chóng.
Tương thích với thiết bị di động
Để làm cho trò chơi ví dụ tương thích với các thiết bị di động, bạn sẽ cần làm việc với các sự kiện cảm ứng chạm chứ không phải là các sự kiện chuột.
Mặc dù một cú chạm nhẹ của ngón tay cũng có thể làm cho các trình duyệt di động hiểu như là một sự kiện nhấn chuột, nói chung dựa vào việc chỉ nghe các sự kiện nhấn chuột trên các trình duyệt di động thường không phải là một cách tiếp cận tốt. Một cách tiếp cận tốt hơn là đính kèm các trình nghe các sự kiện cảm ứng chạm cụ thể để bảo đảm đáp ứng tốt nhất.
Phát hiện các sự kiện cảm ứng chạm
Bạn có thể viết một hàm trình trợ giúp để trước hết phát hiện xem thiết bị có hỗ trợ các sự kiện cảm ứng chạm hay không và sau đó trả về các tọa độ chuột hoặc các tọa độ điểm chạm cho phù hợp. Việc này cho phép gọi các hàm xử lý các tọa độ đầu vào, theo cách không cần biết loại thiết bị đó là gì, bất kể cho dù bạn đang ở trên một máy tính để bàn hoặc nền tảng di động.
Liệt kê 15 cho thấy một ví dụ về một hàm không biết rõ về thiết bị bắt giữ các sự kiện chuột và cảm ứng chạm và chuẩn hóa đáp ứng.
Liệt kê 15. Chuẩn hóa các sự kiện chuột và cảm ứng chạm
function getPosition(e) { var position = {x: null, y: null}; if (Modernizr.touch) { //global variable detecting touch support if (e.touches && e.touches.length > 0) { position.x = e.touches[0].pageX - canvasPosition.x; position.y = e.touches[0].pageY - canvasPosition.y; } } else { position.x = e.pageX - canvasPosition.x; position.y = e.pageY - canvasPosition.y; } return position; }
Để phát hiện sự hỗ trợ cảm ứng chạm, ví dụ này sử dụng thư viện Modernizr (xem phần Tài nguyên). Thư viện Modernizr làm cho việc phát hiện sự hỗ trợ cảm ứng chạm chỉ đơn giản là vấn đề kiểm tra biến Modernizr.touch, biến này trả về giá trị là đúng nếu thiết bị hỗ trợ các sự kiện cảm ứng chạm.
Trình xử lý sự kiện không biết rõ về thiết bị
Trong quá trình khởi tạo ứng dụng, bạn có thể thay thế mã trước đây để định
nghĩa các trình nghe sự kiện bằng một nhánh riêng dùng cho dữ liệu đầu vào
của các thiết bị hỗ trợ cảm ứng chạm và chuột. Khá đơn giản để ánh xạ các
sự kiện chuột tới một sự kiện cảm ứng chạm tương đương. Ví dụ,
mousedown được thay thế bằng
touchstart và
mouseup được thay thế bằng
touchend.
Liệt kê 16 cho thấy một ví dụ về sử dụng Modernizr để ánh xạ các sự kiện
chuột/cảm ứng chạm tương đương. Nó cũng sử dụng hàm
getPosition() được định nghĩa trong
Liệt kê 15.
Liệt kê 16. Sử dụng các sự kiện chuột/cảm ứng chạm đã chuẩn hóa
var eventName = Modernizr.touch ? 'touchstart' : 'click'; canvas.on(eventName, function(e) { e.preventDefault(); var position = getPosition(e); //do something with position here return false; });
Trừ khi bạn cần xử lý các hành động nâng cao hơn, chẳng hạn như dùng hai ngón tay để kéo, đẩy (pinch) và vuốt (swipe), phương thức này thường hoạt động tốt khi dùng một cổng trực tiếp cho các sự kiện chuột từ một ứng dụng máy tính để bàn. Giả định là một hệ thống cảm ứng một điểm chạm duy nhất; nếu cần phát hiện cảm ứng nhiều điểm chạm, nó sẽ đòi hỏi một số mã bổ sung (nằm ngoài phạm vi của bài này).
Kết luận
Trong bài này, bạn đã học cách xử lý các sự kiện bàn phím và chuột và cách hủy bỏ hành vi trình duyệt không mong muốn. Bài này cũng đã thảo luận về các chiến lược để phát quảng bá các sự kiện tới các đối tượng trò chơi và xem xét lại các lý do nâng cao hơn để kiểm tra nhấn chuột và một phương thức đơn giản để giải quyết vấn đề tương thích với di động. Mặc dù phạm vi dữ liệu đầu vào người dùng nằm ngoài bài viết này, các kịch bản dữ liệu đầu vào người dùng điển hình cung cấp một điểm xuất phát theo hướng tạo ra một thư viện không biết rõ về thiết bị và mạnh mẽ để xử lý dữ liệu đầu vào của người dùng cho các ứng dụng HTML5 của bạn.
Tải về
Tài nguyên
Học tập
- "Tạo đồ họa thú vị với phần tử canvas của HTML5 (HTML5 canvas) " (developerWorks, 02.2011): Học cách làm nổi bật các trang web của bạn với Khung nền ảnh (Canvas), một phần tử HTML5 đơn giản mà rất hiệu quả.
- Phần tử canvas của HTML5: Xem trình diễn này để tìm hiểu về sử dụng Canvas API và làm thế nào để mô tả một hình ảnh động rất đơn giản.
- "Những điều cơ bản về HTML5, Phần 4: Chi tiết cuối cùng - Canvas" (developerWorks, Tìm hiểu về phần tử Canvas của HTML5 với một số ví dụ biểu thị các hàm.
- Thao tác điểm ảnh của khung nền ảnh: Trình diễn này của Trung tâm Safari Dev là một ví dụ tuyệt vời về việc quản lý khung nền ảnh để phát triển các tài sản hình ảnh có hiệu quả.
- WHATWG: Khám phá cộng đồng này của các nhà phát triển đang làm với W3C để tinh chỉnh HTML5.
- Hướng dẫn Canvas: Các nhà phát triển Mozilla cho thấy cách thực hiện các phần tử <canvas> trong các trang HTML của bạn.
- Tài liệu tham khảo phần tử canvas của HTML5: Xem các bài tập có ích của W3Schools.com để giúp bạn trau dồi kiến thức về khung nền ảnh của mình.
- API Các sự kiện của jQuery: Tìm hiểu về các phương thức được sử dụng để đăng ký các hành vi có hiệu lực khi người dùng tương tác với trình duyệt và tiếp tục thao tác với các hành vi đã đăng ký đó.
- Vùng phát triển Web của developerWorks: Tìm các bài viết trình bày các giải pháp dựa trên web khác nhau. Xem thư viện kỹ thuật phát triển Web với một loạt các bài viết kỹ thuật và các lời khuyên, các hướng dẫn, các tiêu chuẩn và các sách Đỏ của IBM.
- My developerWorks: Cá nhân hoá kinh nghiệm developerWorks của bạn.
- Webcast và các sự kiện kỹ thuật của developerWorks: Theo sát với công nghệ trong các phiên làm việc này.
- developerWorks trên Twitter: Tham gia hôm nay để theo dõi các mẫu tin tweet trên developerWorks.
- Các trình diễn theo yêu cầu của developerWorks: Xem các trình diễn trải rộng từ cài đặt và thiết lập sản phẩm cho người mới bắt đầu đến các chức năng nâng cao cho các nhà phát triển có kinh nghiệm.
Lấy sản phẩm và công nghệ
- jQuery: Tải về thư viện JavaScript phổ biến làm đơn giản hoá các tương tác duyệt qua tài liệu HTML, xử lý sự kiện, hiệu ứng động và các tương tác Ajax để phát triển web nhanh chóng.
- Modernizr: Nhận thư viện JavaScript nguồn mở để giúp bạn xây dựng thế hệ tiếp theo của các trang web chạy trên HTML5 và CSS3.
- Kibo: Sử dụng thư viện JavaScript đơn giản, phổ biến này để xử lý các sự kiện bàn phím.
- Các phiên bản đánh giá sản phẩm của IBM: Tải về hoặc khám phá các bản dùng thử trực tuyến trong SOA Sandbox của IBM và nhận các công cụ phát triển ứng dụng thực hành của bạn và các sản phẩm phần mềm trung gian từ DB2®, Lotus®, Rational®, Tivoli® và WebSphere®.
Thảo luận
- Cộng đồng developerWorks: Kết nối với những người dùng developerWorks khác trong khi khám phá các blog, các diễn đàn, các nhóm và các wiki theo hướng nhà phát triển.
Bình luận
-
Vùng dữ liệu trên developerWorks
Khám phá các bài viết xoay quanh lĩnh vực dữ liệu, Quản lý hệ thống thông tin
-
Vùng mã nguồn mở trên developerWorks
Xem các bài viết về HTML, CSS, JavaScript,...
-
Điện toán đám mây
Tìm hiểu về điện toán đám mây

markdown:
## Giới thiệu
Các nhà phát triển đã từng làm việc với Flash hay Silverlight thường ngạc nhiên rằng các ứng dụng được viết cho HTML5 Canvas không cung cấp tiện nghi đặc biệt nào về xử lý dữ liệu đầu vào của người dùng. Về cơ bản, đầu vào từ người dùng của HTML liên quan đến việc sử dụng một hệ thống xử lý sự kiện được xây dựng trong các trình duyệt kể từ những ngày đầu tiên mà trình duyệt hỗ trợ JavaScript; không có gì đặc trưng cho HTML5 để phát hiện và xử lý dữ liệu đầu vào từ người dùng. Ví dụ như khả năng cung cấp thông tin phản hồi mức thấp để chỉ ra tọa độ (x, y) mà người dùng đã nhấn chuột vào.
Việc xử lý tương tác của người dùng không có gì khác so với những kiến trúc trò chơi khác. Không có những sự trừu tượng hóa dựng sẵn nào để thông báo cho bạn khi người dùng đã tương tác với một đối tượng cụ thể được biểu thị trên Canvas. Điều này tạo ra một phạm vi rất lớn về kiểm soát mức thấp về cách bạn muốn xử lý các sự kiện đó ra sao. Miễn là bạn có thể không chọn hoặc chọn sai khai báo kiểu tài liệu (DOCTYPE) của trình duyệt khác nhau, thì cuối cùng bạn có thể điều chỉnh việc xử lý các sự kiện để đạt được hiệu quả tối đa theo một ứng dụng duy nhất—chứ không bị gắn chặt vào một cách triển khai thực hiện cụ thể.
Trong bài này, hãy tìm hiểu các kỹ thuật để xử lý tương tác người dùng trong các trò chơi dựa trên HTML Canvas. Các ví dụ minh họa cách xử lý các sự kiện bàn phím, chuột và cảm ứng chạm. Các chiến lược để gửi sự kiện đến các đối tượng trò chơi và khả năng tương thích với thiết bị di động cũng được trình bày trong bài này.
Bạn có thể
[tải về](#download) mã nguồn cho các ví dụ.
## Các loại sự kiện
Sự tương tác của người dùng được xử lý hoàn toàn bằng mô hình lắng nghe sự kiện truyền thống của trình duyệt. Không có gì mới khi HTML5 phát hành; đó vẫn là mô hình sự kiện tương tự như đã được sử dụng từ những ngày đầu của Netscape Navigator.
Về cơ bản, hãy suy nghĩ về một ứng dụng hoặc trò chơi tương tác như là một gắn kết giữa mô hình sự kiện trình duyệt dành cho dữ liệu đầu vào của người dùng và Canvas dành cho đồ họa đầu ra. Không có sự kết nối logic nào giữa hai thứ đó trừ khi bạn tự mình xây dựng nó.
Bạn sẽ tận dụng được lợi thế là những trình lắng nghe sự kiện có thể được
gắn kèm với chính phần tử
<canvas>. Vì phần tử
<canvas> chỉ đơn giản là một phần tử mức-khối
(block-level), về phần trình duyệt điều này không có gì khác hơn việc gắn
kèm các trình nghe sự kiện vào một
<div> hoặc bất kỳ
phần tử mức-khối nào khác.
## Các sự kiện bàn phím
Các loại sự kiện đơn giản nhất để nghe và xử lý là các sự kiện bàn phím. Chúng không phụ thuộc vào phần tử Canvas hoặc vị trí con trỏ của người dùng. Các sự kiện bàn phím chỉ yêu cầu bạn nghe các sự kiện phím xuống, phím lên và nhấn phím ở mức tài liệu.
### Nghe các sự kiện bàn phím
Mô hình trình nghe sự kiện có thể khác nhau tùy thuộc vào việc thực hiện
của trình duyệt, do đó, cách nhanh nhất để dựng lên và chạy là sử dụng một
thư viện để chuẩn hóa việc xử lý các sự kiện. Các ví dụ sau sử dụng jQuery
để kết buộc các sự kiện. Đây thường là cách dễ nhất để bắt đầu, nhưng để
tương thích với các trình duyệt cũ, hiệu năng có thể bị ảnh hưởng do mức
mã dư thừa hoặc mã viết kém có liên quan đến nỗ lực của jQuery. Một
thư viện phổ biến khác, được viết riêng để xử lý sự kiện bàn phím nhanh
chóng giữa các trình duyệt, là Kibo (xem phần
[Tài
nguyên](#resources)).
Liệt kê 1 minh họa việc nghe các sự kiện nhấn phím và có hành động thích hợp dựa trên việc nhấn phím nào.
##### Liệt kê 1. Xử lý các sự kiện bàn phím
$(document.body).on('keydown', function(e) { switch (e.which) { // key code for left arrow case 37: console.log('left arrow key pressed!'); break; // key code for right arrow case 39: console.log('right arrow key pressed!'); break; } });
Nếu ứng dụng của bạn diễn ra trong môi trường của một trình duyệt web, điều quan trọng là luôn để tâm đến các tổ hợp bàn phím nhạy cảm. Trong khi, về kỹ thuật, có thể định nghĩa các hành vi đối với các tổ hợp phím phổ biến nào đó sẽ ghi đè lên các hành vi trình duyệt mặc định của chúng (chẳng hạn như tổ hợp phím CTRL-R), bạn cần tránh điều này.
## Các sự kiện chuột
Các sự kiện chuột phức tạp hơn nhiều so với các sự kiện bàn phím. Bạn phải nhận biết về vị trí của phần tử Canvas bên trong cửa sổ trình duyệt cũng như vị trí con trỏ của người dùng.
### Lắng nghe các sự kiện chuột
Thật dễ dàng để có được vị trí của chuột tương đối so với toàn bộ cửa sổ
trình duyệt bằng cách sử dụng các thuộc tính
e.pageX và
e.pageY. Trong trường hợp này, gốc (0,0) sẽ được đặt ở góc
trên cùng bên trái của toàn bộ cửa sổ trình duyệt.
Bạn thường không quan tâm quá nhiều về dữ liệu đầu vào của người dùng khi con trỏ của người dùng không đặt trong vùng Canvas. Vì vậy, sẽ tốt hơn nếu coi gốc (0,0) được đặt ở góc trên bên trái của phần tử Canvas. Lý tưởng nhất là bạn cần hoạt động bên trong hệ tọa độ cục bộ tương đối so với vùng Canvas chứ không phải là một hệ thống tọa độ chung (global) liên quan đến toàn bộ cửa sổ trình duyệt.
### Các chiến lược về sự kiện chuột
Sử dụng các bước sau đây để chuyển đổi các tọa độ cửa sổ chung sang các tọa độ Canvas cục bộ.
- Tính toán vị trí (x, y) của phần tử DOM của Canvas trên trang web.
- Xác định vị trí chung của chuột liên quan đến toàn bộ tài liệu.
- Để xác định vị trí gốc (0,0) ở góc trên bên trái của phần tử Canvas và chuyển đổi hiệu quả các tọa độ chung thành các tọa độ tương đối, hãy lấy hiệu số giữa vị trí chuột chung được tính ở bước 2 và vị trí Canvas được tính ở bước 1.
Hình 1 cho thấy một ví dụ về thông tin mà bạn cần nắm bắt về mặt hệ tọa độ chung của trang web.
##### Hình 1. Vị trí chuột, các tọa độ chung
![ Một màn hình đang hiển thị một cửa sổ có 300,200 làm các tọa độ x, y của tài liệu và 350,260 làm các tọa độ x, y chung (global)](fig01.jpg)
Hình 2 cho thấy kết quả sau khi chuyển đổi vị trí chuột sang các tọa độ cục bộ.
##### Hình 2. Vị trí chuột sau khi chuyển đổi sang các tọa độ cục bộ
![ Màn hình đang hiển thị các tọa độ x, y cục bộ là 50,60](fig02.jpg)
Liệt kê 2 cho thấy phương thức xác định các tọa độ chuột cục bộ. Giả định
rằng bạn đã định nghĩa một phần tử Canvas theo cách đánh dấu, như sau:
<canvas id="my_canvas"></canvas>.
##### Liệt kê 2. Xử lý các sự kiện chuột
var canvas = $('#my_canvas'); // calculate position of the canvas DOM element on the page var canvasPosition = { x: canvas.offset().left, y: canvas.offset().top }; canvas.on('click', function(e) { // use pageX and pageY to get the mouse position // relative to the browser window var mouse = { x: e.pageX - canvasPosition.x, y: e.pageY - canvasPosition.y } // now you have local coordinates, // which consider a (0,0) origin at the // top-left of canvas element });
## Các hành vi trình duyệt không mong muốn
Trong một trò chơi máy tính, bạn thường không muốn bất kỳ hành vi trình duyệt mặc định nào can thiệp vào các hành động của mình. Ví dụ, bạn không muốn kéo chuột để thực hiện chọn văn bản, một lần nhấn phím chuột phải để mở trình đơn ngữ cảnh hoặc cuộn bánh xe chuột để di chuyển lên và xuống trang.
Hình 3 cho thấy một ví dụ về những gì có thể xảy ra nếu người dùng nhấn phím chuột và kéo một hình ảnh trong trình duyệt. Mặc dù hành vi trình duyệt mặc định hoàn toàn có nghĩa đối với các ứng dụng kéo và thả, những nó không phải là một hành vi mà bạn muốn có trong trò chơi của mình.
##### Hình 3. Hành vi trình duyệt mặc định khi kéo một hình ảnh
![ Một hình ảnh đồ họa đang được di chuyển trong hình ảnh](fig03.jpg)
Trong tất cả các trình xử lý sự kiện, hãy thêm một dòng
preventDefault() và trả về false (sai) từ hàm đó. Mã trong
Liệt kê 3 sẽ thực hiện thủ thuật theo cách ngăn chặn cả hành động mặc định
lẫn sự kiện xảy ra.
##### Liệt kê 3. Ngăn chặn hành vi mặc định
canvas.on('click', function(e) { e.preventDefault(); var mouse = { x: e.pageX - canvasPosition.x, y: e.pageY - canvasPosition.y } //do something with mouse position here return false; });
Ngay cả với mã trong Liệt kê 3, bạn vẫn có thể gặp phải một số tác dụng phụ không mong muốn khi người dùng bắt đầu một sự kiện kéo trên một phần tử DOM, chẳng hạn như sự xuất hiện của con trỏ dạng I (I-beam), sự lựa chọn văn bản và v.v. Theo truyền thống, vấn đề sự kiện kéo phổ biến hơn với các hình ảnh, nhưng để áp dụng nó cho phần tử Canvas để ngăn chặn việc kéo và chọn là một ý tưởng tốt. Liệt kê 4 cho thấy một quy tắc CSS để ngăn chặn các tác dụng phụ của thao tác chọn bằng cách thêm một chút CSS.
##### Liệt kê 4. Các kiểu dáng nên dùng để ngăn chặn thao tác chọn
image, canvas { user-select: none; -ms-user-select: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -webkit-touch-callout: none; -webkit-user-drag: none; }
### Ghi đè lên các hành vi máy tính để bàn
Nói chung, ghi đè lên các sự kiện kéo và chọn là một ý tưởng tốt để đảm bảo rằng hành vi kéo và chọn mặc định của trình duyệt không ngóc đầu dậy.
Mã trong Liệt kê 5 cố ý không sử dụng jQuery để gắn kèm các sự kiện. jQuery
không xử lý đúng đắn các sự kiện
ondragstart và
onselectstart (nếu được gắn kèm bằng cách sử dụng jQuery, thì
các trình xử lý sự kiện có thể không bao giờ bắt đầu được).
##### Liệt kê 5. Hủy bỏ các sự kiện kéo và chọn
var canvasElement = document.getElementById('my_canvas'); // do nothing in the event handler except canceling the event canvasElement.ondragstart = function(e) { if (e && e.preventDefault) { e.preventDefault(); } if (e && e.stopPropagation) { e.stopPropagation(); } return false; } // do nothing in the event handler except canceling the event canvasElement.onselectstart = function(e) { if (e && e.preventDefault) { e.preventDefault(); } if (e && e.stopPropagation) { e.stopPropagation(); } return false; }
### Ghi đè lên các hành vi trên thiết bị di động
Trên các thiết bị di động, điều rất quan trọng là bạn ngăn chặn người dùng phóng đại và mở rộng cửa sổ trình duyệt (việc phóng đại và mở rộng thường là hành vi mặc định của trình duyệt cho thiết bị di động đối với các cử chỉ cảm ứng chạm).
Bạn có thể ngăn chặn hành vi phóng đại bằng cách thêm
user-scalable=no vào siêu thẻ
viewport. Ví
dụ:
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1" />
Để vô hiệu hóa tất cả các chuyển động của tài liệu hoặc cửa sổ khi có các cử chỉ chạm, hãy đính kèm các trình nghe sự kiện trong Liệt kê 6 vào tệp document.body. Về cơ bản, điều này sẽ hủy bỏ tất cả các hành vi trình duyệt mặc định nếu người dùng tình cờ chạm nhẹ vào bất cứ chỗ nào bên ngoài vùng Canvas hoặc vùng trò chơi.
##### Liệt kê 6. Hủy chuyển động cửa sổ trên thiết bị di động
document.body.ontouchstart = function(e) { if (e && e.preventDefault) { e.preventDefault(); } if (e && e.stopPropagation) { e.stopPropagation(); } return false; } document.body.ontouchmove = function(e) { if (e && e.preventDefault) { e.preventDefault(); } if (e && e.stopPropagation) { e.stopPropagation(); } return false; }
## Phát quảng bá tới các đối tượng trò chơi
Bạn cần đính kèm chỉ một trình nghe sự kiện vào Canvas cho mỗi kiểu sự kiện mà bạn muốn bắt giữ. Ví dụ, nếu bạn cần bắt giữ các sự kiện nhấn chuột và di chuyển chuột (mousemove), thì chỉ cần đính kèm một trình nghe sự kiện nhấn chuột duy nhất và một trình nghe sự kiện di chuyển chuột duy nhất vào Canvas. Các trình nghe sự kiện chỉ cần được đính kèm một lần, vì thế, thông thường là đính kèm các sự kiện này trong lúc khởi tạo ứng dụng.
Nếu bạn cần các trình nghe sự kiện bắt giữ bất kỳ thông tin có ích nào lan truyền xuống tới các đối tượng được biểu hiện trên Canvas, bạn phải xây dựng logic riêng của mình cho hệ thống. Trong ví dụ này, một hệ thống như vậy sẽ chịu trách nhiệm phát quảng bá sự kiện nhấn chuột hoặc di chuyển chuột tới tất cả các đối tượng trò chơi liên quan đến việc xử lý một trong những sự kiện đó.
Khi mỗi đối tượng trò chơi học một trong những sự kiện này, đầu tiên đối tượng trò chơi sẽ cần nhận biết xem sự kiện nhấn chuột hoặc di chuyển chuột có liên quan đến chúng không. Nếu có, thì đối tượng trò chơi đó cần xác định xem các tọa độ chuột có vị trí ở bên trong các ranh giới riêng của nó hay không.
### Các chiến lược phát quảng bá
Chiến lược chính xác của bạn sẽ khác nhau dựa trên các kiểu trò chơi. Ví dụ, một bộ các hình ảnh nhỏ hơn dùng trong 2D (2D tileset) có thể có một chiến lược khác so với một thế giới 3D.
Các bước sau đây phác thảo một cách triển khai thực hiện đơn giản có thể làm việc tốt với một ứng dụng 2D đơn giản.
- Phát hiện các tọa độ nhấn chuột của người dùng trong vùng Canvas.
- Thông báo cho tất cả các đối tượng trò chơi rằng một sự kiện nhấn chuột đã xảy ra tại tập hợp các tọa độ cụ thể.
- Đối với mỗi đối tượng trò chơi, thực hiện phép kiểm tra nhấn chuột giữa các tọa độ chuột và hộp giới hạn chứa đối tượng trò chơi để xác định xem các tọa độ chuột có va chạm với đối tượng đó không.
### Ví dụ phát quảng bá đơn giản
Trình xử lý sự kiện nhấn chuột có thể trông giống như Liệt kê 7. Ví dụ này
giả định rằng bạn đã thiết lập một kiểu cấu trúc nào đó để theo dõi tất cả
các đối tượng trò chơi trên thế giới. Vị trí và các chiều của tất cả các
đối tượng trò chơi được lưu trữ trong một biến gọi là
gameObjectArray.
##### Liệt kê 7. Phát quảng bá trình xử lý sự kiện nhấn chuột cho các đối tượng trò chơi
// initialize an array of game objects // at various positions on the screen using // new gameObject(x, y, width, height) var gameObjectArray = [ new gameObject(0, 0, 200, 200), new gameObject(50, 50, 200, 200), new gameObject(500, 50, 100, 100) ]; canvas.on('click', function(e) { var mouse = { x: e.pageX - canvasPosition.x, y: e.pageY - canvasPosition.y } // iterate through all game objects // and call the onclick handler of each for (var i=0; i < gameObjectArray.length; i++) { gameObjectArray[i].handleClick(mouse); } });
Bước tiếp theo là bảo đảm rằng mỗi đối tượng trò chơi có thể thực hiện kiểm tra nhấn chuột để xác định xem tọa độ chuột có va chạm bên trong khu vực hộp giới hạn của các đối tượng trò chơi không. Hình 4 cho thấy một ví dụ về một lần kiểm tra nhấn chuột không thành công.
##### Hình 4. Nhấn chuột ngoài vùng giới hạn - kiểm tra nhấn chuột không thành công
![ Màn hình đang hiển thị một cú nhấn chuột trên các tọa độ x,y là 50,60, khi các tọa độ x, y ranh giới là 120,150](fig04.jpg)
Hình 5 cho thấy một kiểm tra nhấn chuột thành công.
##### Hình 5. Nhấn chuột trong vùng giới hạn - kiểm tra nhấn chuột thành công
![ Màn hình đang hiển thị một cú nhấn chuột trên các tọa độ x,y là 160,170, trong khi các tọa độ x, y ranh giới là 120,150](fig05.jpg)
Bạn có thể định nghĩa một lớp cho các đối tượng trò chơi, như trong Liệt kê
8. Một phép kiểm tra nhấn chuột được thực hiện theo hàm
onclick(), hàm này kiểm tra sự va chạm giữa hộp giới hạn của
đối tượng đó và các tọa độ chuột được chuyển vào làm một tham số.
##### Liệt kê 8. Lớp đối tượng trò chơi và kiểm tra nhấn chuột
function gameObject(x, y, width, height) { this.x = x; this.y = y; this.width = width; this.height = height; // mouse parameter holds the mouse coordinates this.handleClick = function(mouse) { // perform hit test between bounding box // and mouse coordinates if (this.x < mouse.x && this.x + this.width > mouse.x && this.y < mouse.y && this.y + this.height > mouse.y) { // hit test succeeded, handle the click event! return true; } // hit test did not succeed return false; } }
### Cải thiện hiệu quả việc phát quảng bá
Trong nhiều trường hợp, có thể xây dựng một công cụ hiệu quả hơn. Ví dụ, trong một trò chơi với hàng ngàn đối tượng trò chơi, bạn chắc chắn muốn tránh kiểm tra tất cả đối tượng trò chơi đang ở trong hoạt cảnh cứ mỗi lần bắt đầu một sự kiện.
Ví dụ sau đây sử dụng các sự kiện tùy chỉnh của jQuery để gửi một sự kiện tổng hợp. Sự kiện tổng hợp chỉ được xử lý bởi các đối tượng trò chơi nào đang nghe sự kiện cụ thể. Với ví dụ này:
- Xử lý sự kiện nhấn chuột như trước và thực hiện bất kỳ các phép chuyển đổi cần thiết nào (chẳng hạn như chuyển đổi vị trí con chuột theo các tọa độ cục bộ).
- Gửi một sự kiện tổng hợp có chứa các tọa độ chuột đã chuyển đổi làm một tham số.
- Bất kỳ đối tượng trò chơi nào liên quan đến việc xử lý một sự kiện nhấn chuột sẽ thiết lập một trình nghe để nghe sự kiện tổng hợp đó.
Trình xử lý sự kiện nhấn chuột được sửa đổi để chỉ cần kích hoạt một sự
kiện tùy chỉnh. Có thể đặt bất kỳ tên tùy ý nào cho sự kiện tùy chỉnh.
Trong Liệt kê 9, nó được gọi là
handleClick.
##### Liệt kê 9. Khởi động một sự kiện tùy chỉnh
canvas.on('click', function(e) { var mouse= { x: e.pageX - canvasPosition.x, y: e.pageY - canvasPosition.y } //fire off synthetic event containing mouse coordinate info $(canvas).trigger('handleClick', [mouse]); });
Như hiển thị trong Liệt kê 10, lớp đối tượng trò chơi cũng được sửa đổi.
Thay vì định nghĩa một hàm
onclick, chỉ cần nghe sự kiện
handleClick. Bất cứ khi nào khởi động sự kiện
handleClick bất kỳ các đối tượng trò chơi nào đang nghe sự
kiện đó sẽ bắt đầu các trình xử lý sự kiện tương ứng của chúng.
##### Liệt kê 10. Xử lý một sự kiện tùy chỉnh
function gameObject(x, y, width, height) { var self = this; this.x = x; this.y = y; this.width = width; this.height = height; $(canvas).on('handleClick', function(e, mouse) { // perform hit test between bounding box // and mouse coordinates if (self.x < mouse.x && self.x + self.width > mouse.x && self.y < mouse.y && self.y + self.height > mouse.y) { // hit test succeeded, handle the click event! } }); }
## Kiểm tra nhấn chuột nâng cao
Điều quan trọng là xem xét những gì sẽ xảy ra khi một số đối tượng trò chơi được xếp chồng lên nhau. Nếu người dùng nhấn chuột vào một điểm, ở đây một số đối tượng trò chơi được xếp chồng lên nhau, bạn sẽ cần xác định cách xử lý các hành vi như thế nào. Ví dụ, bạn thường hy vọng chỉ có trình xử lý sự kiện của đối tượng gần nhất được khởi động còn các đối tượng khác bên dưới nó được bỏ qua.
Để xử lý việc xếp chồng lên nhau như vậy, bạn cần phải biết thứ tự, hoặc độ sâu, của mỗi đối tượng trò chơi nằm trong chồng đó. Canvas không trưng ra bất kỳ biểu diễn logic nào của độ sâu, vì vậy một lần nữa, bạn cần đặt ra các quy tắc và tạo ra logic cần thiết để xử lý tình trạng này.
Để đưa vào khái niệm về độ sâu, việc gán một chỉ mục-z cho tất cả các đối tượng trò chơi để mô tả độ sâu của chúng là cần thiết. Liệt kê 11 cho thấy một ví dụ.
##### Liệt kê 11. Thêm một chỉ mục-z cho đối tượng Trò chơi
function gameObject(x, y, zIndex, width, height) { var self = this; this.x = x; this.y = y; this.zIndex = zIndex; this.width = width; this.height = height; //... }
Để giúp cho việc kiểm tra độ sâu dễ dàng, bạn cần phải thực hiện việc sắp xếp. Trong Liệt kê 12, cấu trúc ví dụ để lưu trữ các đối tượng trò chơi được sắp xếp sao cho đối tượng trò chơi có chỉ mục-z cao nhất sẽ xuất hiện đầu tiên trong danh sách.
##### Liệt kê 12. Sắp xếp mảng đối tượng trò chơi
// sort in order such that highest z-index occurs first var sortedGameObjectArray = gameObjectArray.sort(function(gameObject1, gameObject2) { if (gameObject1.zIndex < gameObject2.zIndex) return true; else return false; })
Cuối cùng, trong hàm
click, hãy thay đổi mọi thứ để lặp qua
tất cả các đối tượng trò chơi trong mảng đã sắp xếp.
Ngay sau khi bạn nhận được một kết quả đúng từ việc kiểm tra nhấn chuột của
một đối tượng trò chơi, hãy ngừng ngay lập tức, không cho việc nhấn chuột
tiếp tục lan truyền. Nếu bạn không dừng ngay việc kiểm tra này, như trong
Liệt kê 13, thì hành vi không mong muốn của các đối tượng trò chơi với
việc xử lý sự kiện
click (nhấn chuột) ở độ sâu hơn sẽ vẫn
tiếp tục.
##### Liệt kê 13. Ngừng ngay việc kiểm tra nhấn chuột thành công
canvas.on('click', function(e) { var mouse = { x: e.pageX - canvasPosition.x, y: e.pageY - canvasPosition.y } for (var i=0; i < sortedGameObjectArray.length; i++) { var hitTest = sortedGameObjectArray[i].onclick(mouse); // stop as soon as one hit test succeeds if (hitTest) { break; // break out of the hit test } } });
### Các ranh giới của đối tượng trò chơi không theo quy tắc
Mặc dù để thực hiện kiểm tra nhấn chuột dựa vào các hộp giới hạn hình chữ nhật thường đơn giản nhất và hiệu quả nhất, những trong nhiều trường hợp điều đó là chưa đủ. Nếu đối tượng trò chơi có hình dạng bất thường nhiều hơn, thì để kiểm tra dựa vào một vùng giới hạn hình tam giác hoặc đa giác có thể có ý nghĩa hơn. Trong những trường hợp như vậy, bạn sẽ cần thay logic kiểm tra nhấn chuột trong trình xử lý sự kiện của đối tượng trò chơi bằng hình thức phát hiện nhấn chuột nâng cao. Thông thường, bạn cần tham khảo lĩnh vực của vật lý va chạm trò chơi để biết logic phù hợp.
API Canvas cung cấp một hàm thú vị được gọi
IsPointInPath(),
có thể thực hiện các bài kiểm tra va chạm trong vùng đa giác. Về cơ bản,
IsPointInPath(x, y) cho phép bạn kiểm tra xem điểm (x, y) cụ
thể có nằm bên trong một đường tùy ý (về cơ bản là đường ranh giới đa
giác) hay không. Nó sẽ trả về kết quả là đúng nếu tọa độ (x, y) rơi vào
bên trong đường hiện tại, được định nghĩa trong bối cảnh Canvas.
### Sử dụng hàm
isPointInPath()
Hình 6 cho thấy một tình huống, ở đó cần kiểm tra các tọa độ chuột dựa vào một đường không phải là hình chữ nhật. Trong trường hợp này, đó là một đường hình tam giác đơn giản.
##### Hình 6. Nhấn chuột trong ranh giới của một đường hình tam giác
![ Một hình tam giác đen với một con trỏ chuột đang nhấn trên các tọa độ x, y là 20,50 x,y](fig06.jpg)
Đường phủ kín mãu đen được hiển thị trực quan chỉ dùng cho mục đích minh
họa. Vì đường này không phải được hiển thị về vật lý trên màn hình để hàm
IsPointInPath() trả về một kết quả có ích, nên chỉ cần định
nghĩa đường này mà không bao giờ thực hiện một cuộc gọi tới hàm
fill() hay
stroke() để thực sự vẽ ra đường đó.
Liệt kê 14 cho thấy các chi tiết.
##### Liệt kê 14. Sử dụng hàm
isPointInPath để phát hiện nhấn chuột
$(canvas).on('handleClick', function(e, mouse) { // first, define polygonal bounding area as a path context.save(); context.beginPath(); context.moveTo(0,0); context.lineTo(0,100); context.lineTo(100,100); context.closePath(); // do not actually fill() or stroke() the path because // the path only exists for purposes of hit testing // context.fill(); // perform hit test between irregular bounding area // and mouse coordinates if (context.isPointInPath(mouse.x, mouse.y)) { // hit test succeeded, handle the click event! } context.restore(); });
Mặc dù để viết các thuật toán va chạm của chính bạn thường có hiệu quả hơn
chứ không phải bằng cách sử dụng hàm
IsPointInPath(), nó có
thể là một công cụ tốt để tạo nguyên mẫu và phát triển nhanh chóng.
## Tương thích với thiết bị di động
Để làm cho trò chơi ví dụ tương thích với các thiết bị di động, bạn sẽ cần làm việc với các sự kiện cảm ứng chạm chứ không phải là các sự kiện chuột.
Mặc dù một cú chạm nhẹ của ngón tay cũng có thể làm cho các trình duyệt di động hiểu như là một sự kiện nhấn chuột, nói chung dựa vào việc chỉ nghe các sự kiện nhấn chuột trên các trình duyệt di động thường không phải là một cách tiếp cận tốt. Một cách tiếp cận tốt hơn là đính kèm các trình nghe các sự kiện cảm ứng chạm cụ thể để bảo đảm đáp ứng tốt nhất.
### Phát hiện các sự kiện cảm ứng chạm
Bạn có thể viết một hàm trình trợ giúp để trước hết phát hiện xem thiết bị có hỗ trợ các sự kiện cảm ứng chạm hay không và sau đó trả về các tọa độ chuột hoặc các tọa độ điểm chạm cho phù hợp. Việc này cho phép gọi các hàm xử lý các tọa độ đầu vào, theo cách không cần biết loại thiết bị đó là gì, bất kể cho dù bạn đang ở trên một máy tính để bàn hoặc nền tảng di động.
Liệt kê 15 cho thấy một ví dụ về một hàm không biết rõ về thiết bị bắt giữ các sự kiện chuột và cảm ứng chạm và chuẩn hóa đáp ứng.
##### Liệt kê 15. Chuẩn hóa các sự kiện chuột và cảm ứng chạm
function getPosition(e) { var position = {x: null, y: null}; if (Modernizr.touch) { //global variable detecting touch support if (e.touches && e.touches.length > 0) { position.x = e.touches[0].pageX - canvasPosition.x; position.y = e.touches[0].pageY - canvasPosition.y; } } else { position.x = e.pageX - canvasPosition.x; position.y = e.pageY - canvasPosition.y; } return position; }
Để phát hiện sự hỗ trợ cảm ứng chạm, ví dụ này sử dụng thư viện Modernizr
(xem phần
[Tài nguyên](#resources)). Thư viện Modernizr làm cho
việc phát hiện sự hỗ trợ cảm ứng chạm chỉ đơn giản là vấn đề kiểm tra biến
Modernizr.touch, biến này trả về giá trị là đúng nếu thiết bị hỗ trợ các
sự kiện cảm ứng chạm.
### Trình xử lý sự kiện không biết rõ về thiết bị
Trong quá trình khởi tạo ứng dụng, bạn có thể thay thế mã trước đây để định
nghĩa các trình nghe sự kiện bằng một nhánh riêng dùng cho dữ liệu đầu vào
của các thiết bị hỗ trợ cảm ứng chạm và chuột. Khá đơn giản để ánh xạ các
sự kiện chuột tới một sự kiện cảm ứng chạm tương đương. Ví dụ,
mousedown được thay thế bằng
touchstart và
mouseup được thay thế bằng
touchend.
Liệt kê 16 cho thấy một ví dụ về sử dụng Modernizr để ánh xạ các sự kiện
chuột/cảm ứng chạm tương đương. Nó cũng sử dụng hàm
getPosition() được định nghĩa trong
[Liệt kê 15](#list15).
##### Liệt kê 16. Sử dụng các sự kiện chuột/cảm ứng chạm đã chuẩn hóa
var eventName = Modernizr.touch ? 'touchstart' : 'click'; canvas.on(eventName, function(e) { e.preventDefault(); var position = getPosition(e); //do something with position here return false; });
Trừ khi bạn cần xử lý các hành động nâng cao hơn, chẳng hạn như dùng hai ngón tay để kéo, đẩy (pinch) và vuốt (swipe), phương thức này thường hoạt động tốt khi dùng một cổng trực tiếp cho các sự kiện chuột từ một ứng dụng máy tính để bàn. Giả định là một hệ thống cảm ứng một điểm chạm duy nhất; nếu cần phát hiện cảm ứng nhiều điểm chạm, nó sẽ đòi hỏi một số mã bổ sung (nằm ngoài phạm vi của bài này).
## Kết luận
Trong bài này, bạn đã học cách xử lý các sự kiện bàn phím và chuột và cách hủy bỏ hành vi trình duyệt không mong muốn. Bài này cũng đã thảo luận về các chiến lược để phát quảng bá các sự kiện tới các đối tượng trò chơi và xem xét lại các lý do nâng cao hơn để kiểm tra nhấn chuột và một phương thức đơn giản để giải quyết vấn đề tương thích với di động. Mặc dù phạm vi dữ liệu đầu vào người dùng nằm ngoài bài viết này, các kịch bản dữ liệu đầu vào người dùng điển hình cung cấp một điểm xuất phát theo hướng tạo ra một thư viện không biết rõ về thiết bị và mạnh mẽ để xử lý dữ liệu đầu vào của người dùng cho các ứng dụng HTML5 của bạn.
## Tải về
|Mô tả||Tên||Kích thước|
|Article code listings|
## Tài nguyên
### Học tập
- "
[Tạo đồ họa thú vị với phần tử canvas của HTML5 (HTML5 canvas)](http://www.ibm.com/developerworks/web/library/wa-html5canvas/?ca=drs-)" (developerWorks, 02.2011): Học cách làm nổi bật các trang web của bạn với Khung nền ảnh (Canvas), một phần tử HTML5 đơn giản mà rất hiệu quả. [Phần tử canvas của HTML5](http://www.ibm.com/developerworks/offers/lp/demos/summary/web-jhtml5canvas.html?S_TACT=105AGY75&ca=drs-): Xem trình diễn này để tìm hiểu về sử dụng Canvas API và làm thế nào để mô tả một hình ảnh động rất đơn giản.
- "
[Những điều cơ bản về HTML5, Phần 4: Chi tiết cuối cùng - Canvas](https://www.ibm.com/developerworks/vn/library/wa-html5fundamentals4/)" (developerWorks, Tìm hiểu về phần tử Canvas của HTML5 với một số ví dụ biểu thị các hàm. [Thao tác điểm ảnh của khung nền ảnh](https://developer.apple.com/safaridemos/canvas-pixel.php): Trình diễn này của Trung tâm Safari Dev là một ví dụ tuyệt vời về việc quản lý khung nền ảnh để phát triển các tài sản hình ảnh có hiệu quả. [WHATWG](http://www.whatwg.org/): Khám phá cộng đồng này của các nhà phát triển đang làm với W3C để tinh chỉnh HTML5. [Hướng dẫn Canvas](https://developer.mozilla.org/en/Canvas_tutorial): Các nhà phát triển Mozilla cho thấy cách thực hiện các phần tử <canvas> trong các trang HTML của bạn. [Tài liệu tham khảo phần tử canvas của HTML5](http://www.w3schools.com/html5/html5_canvas.asp): Xem các bài tập có ích của W3Schools.com để giúp bạn trau dồi kiến thức về khung nền ảnh của mình. [API Các sự kiện của jQuery](http://api.jquery.com/category/events): Tìm hiểu về các phương thức được sử dụng để đăng ký các hành vi có hiệu lực khi người dùng tương tác với trình duyệt và tiếp tục thao tác với các hành vi đã đăng ký đó. [Vùng phát triển Web của developerWorks](http://www.ibm.com/developerworks/web/?S_TACT=105AGY75): Tìm các bài viết trình bày các giải pháp dựa trên web khác nhau. Xem [thư viện kỹ thuật phát triển Web](http://www.ibm.com/developerworks/views/web/libraryview.jsp?S_TACT=105AGY75)với một loạt các bài viết kỹ thuật và các lời khuyên, các hướng dẫn, các tiêu chuẩn và các sách Đỏ của IBM. [My developerWorks](http://www.ibm.com/developerworks/mydeveloperworks?S_TACT=105AGY75): Cá nhân hoá kinh nghiệm developerWorks của bạn. [Webcast và các sự kiện kỹ thuật của developerWorks](http://www.ibm.com/developerworks/offers/techbriefings/?S_TACT=105AGY75): Theo sát với công nghệ trong các phiên làm việc này. [developerWorks trên Twitter](http://twitter.com/developerworks/): Tham gia hôm nay để theo dõi các mẫu tin tweet trên developerWorks. [Các trình diễn theo yêu cầu của developerWorks](http://www.ibm.com/developerworks/offers/lp/demos/?S_TACT=105AGY75): Xem các trình diễn trải rộng từ cài đặt và thiết lập sản phẩm cho người mới bắt đầu đến các chức năng nâng cao cho các nhà phát triển có kinh nghiệm.
### Lấy sản phẩm và công nghệ
[jQuery](http://jquery.com): Tải về thư viện JavaScript phổ biến làm đơn giản hoá các tương tác duyệt qua tài liệu HTML, xử lý sự kiện, hiệu ứng động và các tương tác Ajax để phát triển web nhanh chóng. [Modernizr](http://modernizr.com): Nhận thư viện JavaScript nguồn mở để giúp bạn xây dựng thế hệ tiếp theo của các trang web chạy trên HTML5 và CSS3. [Kibo](https://github.com/marquete/kibo): Sử dụng thư viện JavaScript đơn giản, phổ biến này để xử lý các sự kiện bàn phím. [Các phiên bản đánh giá sản phẩm của IBM](http://www.ibm.com/developerworks/downloads/?S_TACT=105AGY75): Tải về hoặc [khám phá các bản dùng thử trực tuyến trong SOA Sandbox của IBM](http://www.ibm.com/developerworks/downloads/soasandbox/?S_TACT=105AGY75)và nhận các công cụ phát triển ứng dụng thực hành của bạn và các sản phẩm phần mềm trung gian từ DB2®, Lotus®, Rational®, Tivoli® và WebSphere®.
### Thảo luận
[Cộng đồng developerWorks](http://www.ibm.com/developerworks/community?S_TACT=105AGY75): Kết nối với những người dùng developerWorks khác trong khi khám phá các blog, các diễn đàn, các nhóm và các wiki theo hướng nhà phát triển.
## Bình luận
[Vùng dữ liệu trên developerWorks](http://www.ibm.com/developerworks/vn/views/data/libraryview.jsp)
Khám phá các bài viết xoay quanh lĩnh vực dữ liệu, Quản lý hệ thống thông tin
[Vùng mã nguồn mở trên developerWorks](http://www.ibm.com/developerworks/vn/views/opensource/libraryview.jsp)
Xem các bài viết về HTML, CSS, JavaScript,...
[Điện toán đám mây](http://www.ibm.com/developerworks/vn/views/cloud/libraryview.jsp)
Tìm hiểu về điện toán đám mây

WARC-Type: response

Content-Type: application/http; msgtype=response

Content-Encoding: gzip

Content-Type: text/html; charset=UTF-8

Content-Language: en-US

Connection: close

Vary: Accept-Encoding