Canvas vẽ bitmap (pixel) - không có DOM element cho từng hình vẽ (hình tròn, chữ nhật, path...). Để xử lý click trên hình vẽ, bạn phải:
- Gắn sự kiện click cho toàn bộ canvas element.
- Tính toán tọa độ click tương đối so với canvas.
- Kiểm tra xem tọa độ đó có nằm trong vùng hình vẽ nào không (hit testing).
Cơ bản: Gắn sự kiện click cho canvas
var canvas = document.getElementById('myCanvas');
canvas.addEventListener('click', function(event) {
// Tính tọa độ click tương đối so với canvas
var rect = canvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
console.log('Click tại:', x, y);
// TODO: Kiểm tra hit test cho các hình vẽ
}, false);
Hit Testing - Kiểm tra va chạm
Lưu trữ thông tin hình vẽ (vị trí, kích thước) trong mảng, kiểm tra khi click:
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
var rect = canvas.getBoundingClientRect();
// Mảng lưu trữ các hình vẽ
var shapes = [];
// Thêm hình chữ nhật
shapes.push({
type: 'rect',
x: 50, y: 50,
width: 100, height: 80,
color: '#1C2128',
id: 'rect1'
});
// Thêm hình tròn
shapes.push({
type: 'circle',
x: 200, y: 100,
radius: 40,
color: '#e74c3c',
id: 'circle1'
});
// Vẽ tất cả hình
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
shapes.forEach(function(shape) {
ctx.fillStyle = shape.color;
if (shape.type === 'rect') {
ctx.fillRect(shape.x, shape.y, shape.width, shape.height);
} else if (shape.type === 'circle') {
ctx.beginPath();
ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2);
ctx.fill();
}
});
}
render();
// Xử lý click
canvas.addEventListener('click', function(event) {
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
// Kiểm tra ngược từ trên xuống (topmost first)
for (var i = shapes.length - 1; i >= 0; i--) {
var shape = shapes[i];
var hit = false;
if (shape.type === 'rect') {
hit = x > shape.x && x < shape.x + shape.width &&
y > shape.y && y < shape.y + shape.height;
} else if (shape.type === 'circle') {
var dx = x - shape.x;
var dy = y - shape.y;
hit = (dx * dx + dy * dy) < (shape.radius * shape.radius);
}
if (hit) {
alert('Clicked on: ' + shape.id);
break; // Chỉ xử lý hình trên cùng
}
}
});
Nâng cao: Sử dụng Path2D (HTML5 Canvas API)
Path2D lưu trữ đường vẽ (path) và hỗ trợ ctx.isPointInPath(path, x, y) cho hit test chính xác cho path phức tạp:
var path1 = new Path2D();
path1.rect(50, 50, 100, 80); // Hình chữ nhật
var path2 = new Path2D();
path2.arc(200, 100, 40, 0, Math.PI * 2); // Hình tròn
// Vẽ
ctx.fillStyle = '#1C2128';
ctx.fill(path1);
ctx.fillStyle = '#e74c3c';
ctx.fill(path2);
// Hit test chính xác theo path
canvas.addEventListener('click', function(e) {
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
if (ctx.isPointInPath(path2, x, y)) { // Kiểm tra hình tròn trước (trên cùng)
alert('Click hình tròn');
} else if (ctx.isPointInPath(path1, x, y)) {
alert('Click hình chữ nhật');
}
});
Thư viện hỗ trợ (Khuyên dùng cho app phức tạp)
- Fabric.js - Object model cho canvas, event handling built-in.
- Konva.js - 2D canvas framework, hiệu năng cao, React/Vue binding.
- Paper.js - Vector graphics, scene graph.
- Two.js - API thống nhất cho Canvas, SVG, WebGL.