Berikut ini catatan belajar saya untuk membuat obyek yang menempel ke tepi obyek lain. Detail penjelasan kode dan cara menggunakannya. Kode di bawah ini bisa langsung disalin ke notepad atau notepad++, kemudian disimpan dalam format html.
Visual contoh yang akan muncul seperti dalam contoh berikut. Silakan buka dengan browser yang terdapat di komputer, misal menggunakan Chrome, Firefox, Edge, Opera, dan lain-lain.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Konva.js v9 - Snap + Centered Text</title>
<script src="https://unpkg.com/konva@9.0.0/konva.min.js"></script>
<style>
/* Gaya dasar untuk halaman */
body {
margin: 0; /* Hilangkan margin default */
padding: 20px; /* Beri jarak 20px dari tepi */
background: #f0f0f0; /* Warna latar belakang abu-abu muda */
}
/* Gaya untuk container canvas */
#container {
background: white; /* Latar belakang putih */
box-shadow: 0 0 10px rgba(0,0,0,0.1); /* Bayangan halus */
}
</style>
</head>
<body>
<!-- Div container untuk canvas Konva -->
<div id="container"></div>
<script>
// =============== KONFIGURASI DASAR ===============
const width = 400; // Lebar canvas dalam piksel
const height = 400; // Tinggi canvas dalam piksel
const center = {
x: width / 2, // Titik tengah horizontal
y: height / 2 // Titik tengah vertikal
};
const SNAP_THRESHOLD = 15; // Jarak maksimum untuk snap (dalam piksel)
// Konfigurasi lingkaran utama (kuning)
const referenceCircle = {
x: center.x, // Posisi horizontal pusat lingkaran
y: center.y, // Posisi vertikal pusat lingkaran
radius: 50, // Jari-jari lingkaran
};
// =============== TITIK SNAP (8 TITIK DI TEPI) ===============
const SNAP_COUNT = 8; // Jumlah titik snap (setiap 45 derajat)
const snapPoints = Array.from({ length: SNAP_COUNT }).map((_, i) => {
// Hitung sudut dalam radian untuk setiap titik
const angle = (i * (360 / SNAP_COUNT)) * (Math.PI / 180);
return {
// Hitung posisi X menggunakan cosinus
x: referenceCircle.x + Math.cos(angle) * referenceCircle.radius,
// Hitung posisi Y menggunakan sinus
y: referenceCircle.y + Math.sin(angle) * referenceCircle.radius,
};
});
// =============== INISIALISASI CANVAS ===============
// Buat stage (kanvas utama)
const stage = new Konva.Stage({
container: 'container', // Elemen HTML yang akan menampung canvas
width: width, // Lebar stage
height: height, // Tinggi stage
});
// Buat layer (lapisan untuk menampung objek)
const layer = new Konva.Layer();
stage.add(layer); // Tambahkan layer ke stage
// =============== LINGKARAN KUNING UTAMA ===============
const yellowCircle = new Konva.Circle({
x: referenceCircle.x, // Posisi horizontal pusat
y: referenceCircle.y, // Posisi vertikal pusat
radius: referenceCircle.radius, // Ukuran jari-jari
fill: 'yellow', // Warna isi kuning
stroke: 'black', // Warna garis tepi
strokeWidth: 2, // Ketebalan garis tepi
});
layer.add(yellowCircle); // Tambahkan ke layer pertama
// Teks di tengah lingkaran kuning
const yellowCircleText = new Konva.Text({
x: referenceCircle.x - referenceCircle.radius, // Posisi X mulai dari kiri lingkaran
y: referenceCircle.y - referenceCircle.radius, // Posisi Y mulai dari atas lingkaran
width: referenceCircle.radius * 2, // Lebar area teks = diameter lingkaran
height: referenceCircle.radius * 2, // Tinggi area teks = diameter lingkaran
text: "PUSAT", // Konten teks
fontSize: 24, // Ukuran font
fontFamily: 'Arial', // Jenis font
fill: 'red', // Warna teks merah
align: 'center', // Rata tengah horizontal
verticalAlign: 'middle', // Rata tengah vertikal
listening: false, // Nonaktifkan interaksi mouse
});
// =============== TITIK SNAP MERAH ===============
snapPoints.forEach(point => {
const snapDot = new Konva.Circle({
x: point.x, // Posisi X dari array snapPoints
y: point.y, // Posisi Y dari array snapPoints
radius: 5, // Ukuran titik snap
fill: 'red', // Warna merah
opacity: 0.7, // Sedikit transparan
});
layer.add(snapDot); // Tambahkan titik ke layer
});
// =============== LINGKARAN BIRU YANG BISA DI-DRAG ===============
const blueCircle = new Konva.Circle({
x: 100, // Posisi awal X
y: 100, // Posisi awal Y
radius: 16, // Ukuran lebih kecil dari lingkaran kuning
fill: 'blue', // Warna biru
draggable: true, // Bisa di-drag
shadowBlur: 10, // Efek bayangan blur
shadowOpacity: 0.5, // Opasitas bayangan
});
// Teks di dalam lingkaran biru
const blueCircleText = new Konva.Text({
x: blueCircle.x() - blueCircle.radius(), // Posisi X relatif terhadap lingkaran
y: blueCircle.y() - blueCircle.radius(), // Posisi Y relatif terhadap lingkaran
width: blueCircle.radius() * 2, // Lebar area teks
height: blueCircle.radius() * 2, // Tinggi area teks
text: "DRAG ME", // Teks awal
fontSize: 10, // Ukuran font lebih kecil
fontFamily: 'Arial', // Jenis font
fill: 'white', // Warna teks putih
align: 'center', // Rata tengah horizontal
verticalAlign: 'middle', // Rata tengah vertikal
listening: false, // Nonaktifkan interaksi
});
// =============== FUNGSI SNAP KETIKA DI-DRAG ===============
blueCircle.on('dragmove', () => {
let snapped = false; // Flag untuk menandai apakah sudah snap
// Cek semua titik snap
snapPoints.forEach(point => {
// Hitung jarak antara lingkaran biru dan titik snap
const distance = Math.hypot(
blueCircle.x() - point.x,
blueCircle.y() - point.y
);
// Jika jarak cukup dekat, lakukan snap
if (distance < SNAP_THRESHOLD && !snapped) {
blueCircle.position(point); // Pindahkan ke titik snap
blueCircle.fill('green'); // Ubah warna jadi hijau
blueCircleText.text("SNAPPED!"); // Ubah teks
snapped = true; // Set flag menjadi true
}
});
// Update posisi teks mengikuti lingkaran biru
blueCircleText.position({
x: blueCircle.x() - blueCircle.radius(),
y: blueCircle.y() - blueCircle.radius(),
});
// Jika tidak snap, kembalikan ke keadaan semula
if (!snapped) {
blueCircle.fill('blue');
blueCircleText.text("DRAG ME");
}
layer.batchDraw(); // Gambar ulang layer
});
// =============== MENAMBAHKAN SEMUA OBJEK KE LAYER ===============
layer.add(
yellowCircle, // Lingkaran kuning (paling bawah)
yellowCircleText, // Teks di lingkaran kuning
blueCircle, // Lingkaran biru
blueCircleText // Teks di lingkaran biru
);
layer.draw(); // Gambar awal
</script>
</body>
</html>
Tidak ada komentar:
Posting Komentar