Canvas Interaktif — Creative Web

Canvas Interaktif Canvas tidak punya built-in event handling untuk objek individual (tidak seperti HTML/SVG). Kamu harus menangani interaksi secara manual —…

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!