Canvas Interaktif
Canvas tidak punya built-in event handling untuk objek individual (tidak seperti HTML/SVG). Kamu harus menangani interaksi secara manual — tapi justru ini yang memberi kontrol penuh.
Mendapatkan Posisi Mouse di Canvas
Event mouse memberikan koordinat relatif terhadap halaman, bukan canvas. Kita perlu konversi:
canvas.addEventListener("mousemove", (e) => {
const rect = canvas.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
console.log(mouseX, mouseY);
});
getBoundingClientRect() memberikan posisi canvas di halaman, lalu kita kurangi untuk mendapat posisi relatif di dalam canvas.
Hit Detection — Apakah Mouse di Atas Objek?
Untuk mendeteksi apakah mouse berada di atas sebuah lingkaran:
function isInsideCircle(mouseX, mouseY, circle) {
const dx = mouseX - circle.x;
const dy = mouseY - circle.y;
const distance = Math.sqrt(dx * dx + dy * dy);
return distance < circle.radius;
}
// Untuk persegi lebih mudah:
function isInsideRect(mouseX, mouseY, rect) {
return mouseX > rect.x && mouseX < rect.x + rect.width &&
mouseY > rect.y && mouseY < rect.y + rect.height;
}
Hover Effect pada Canvas Object
const circles = [
{ x: 100, y: 100, radius: 40, color: "#3498db", hover: false },
{ x: 250, y: 150, radius: 30, color: "#e74c3c", hover: false },
{ x: 400, y: 100, radius: 50, color: "#2ecc71", hover: false },
];
canvas.addEventListener("mousemove", (e) => {
const rect = canvas.getBoundingClientRect();
const mx = e.clientX - rect.left;
const my = e.clientY - rect.top;
circles.forEach(c => {
c.hover = isInsideCircle(mx, my, c);
});
// Ubah cursor
canvas.style.cursor = circles.some(c => c.hover) ? "pointer" : "default";
});
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
circles.forEach(c => {
ctx.beginPath();
ctx.arc(c.x, c.y, c.hover ? c.radius + 5 : c.radius, 0, Math.PI * 2);
ctx.fillStyle = c.color;
ctx.globalAlpha = c.hover ? 1 : 0.7;
ctx.fill();
});
ctx.globalAlpha = 1;
requestAnimationFrame(draw);
}
draw();
Drag & Drop di Canvas
let isDragging = false;
let dragTarget = null;
let offsetX, offsetY;
canvas.addEventListener("mousedown", (e) => {
const rect = canvas.getBoundingClientRect();
const mx = e.clientX - rect.left;
const my = e.clientY - rect.top;
// Cari objek yang diklik (dari atas ke bawah)
for (let i = circles.length - 1; i >= 0; i--) {
if (isInsideCircle(mx, my, circles[i])) {
isDragging = true;
dragTarget = circles[i];
offsetX = mx - dragTarget.x;
offsetY = my - dragTarget.y;
break;
}
}
});
canvas.addEventListener("mousemove", (e) => {
if (!isDragging || !dragTarget) return;
const rect = canvas.getBoundingClientRect();
dragTarget.x = e.clientX - rect.left - offsetX;
dragTarget.y = e.clientY - rect.top - offsetY;
});
canvas.addEventListener("mouseup", () => {
isDragging = false;
dragTarget = null;
});
Touch Events untuk Mobile
Canvas juga mendukung sentuhan. Touch event sedikit berbeda dari mouse:
canvas.addEventListener("touchstart", (e) => {
e.preventDefault(); // cegah scroll
const touch = e.touches[0];
const rect = canvas.getBoundingClientRect();
const tx = touch.clientX - rect.left;
const ty = touch.clientY - rect.top;
// ... handle sama seperti mousedown
});
canvas.addEventListener("touchmove", (e) => {
e.preventDefault();
const touch = e.touches[0];
// ... handle sama seperti mousemove
});
canvas.addEventListener("touchend", () => {
// ... handle sama seperti mouseup
});
Kenapa preventDefault() wajib di touch events? Default browser ketika jari bergerak di layar adalah scroll halaman atau pinch-to-zoom. Kalau kamu bikin canvas drawing / game, scroll ini bakal ganggu interaksi — user mau gambar, halamannya malah ikut gerak. preventDefault() bilang browser "saya handle event ini, jangan scroll".
Catatan: Sejak Chrome 56+, listener touch default bersifat
passive: true(tidak bisa preventDefault). Kalau kamu butuh preventDefault, lampirkan opsi{ passive: false }:canvas.addEventListener("touchstart", handler, { passive: false });
Koordinat Conversion untuk Canvas yang Di-Scale
Jika canvas di-scale via CSS, koordinat perlu disesuaikan:
function getCanvasCoords(e) {
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
return {
x: (e.clientX - rect.left) * scaleX,
y: (e.clientY - rect.top) * scaleY
};
}
Dengan menguasai interaksi canvas, kamu bisa membuat game, drawing app, atau visualisasi data interaktif!