Membuat Obyek yang Menempel ke Obyek lain Menggunakan Konva.js

Rabu, 21 Mei 2025 edit

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>
Bagikan di

Tidak ada komentar:

Posting Komentar

 
Copyright © 2015-2025 Urip dot Info | Disain Template oleh Herdiansyah Dimodivikasi Urip.Info