IKLAN. hantamo.com
scroll untuk melihat konten

Cara Membuat Heatmap Sederhana dengan JavaScript

09/02/26

Cara Membuat Heatmap Sederhana dengan JavaScript

Dalam era data-driven seperti sekarang, kemampuan untuk memvisualisasikan informasi kompleks menjadi kunci pengambilan keputusan. Heatmap, atau peta panas, telah berkembang dari sekadar alat analitik web menjadi visualisasi serbaguna untuk berbagai data spasial dan kepadatan—mulai dari intensitas curah hujan, kepadatan penduduk, hingga interaksi pengguna pada antarmuka. Dengan kekuatan JavaScript modern di tahun 2025, membuat visualisasi heatmap yang interaktif dan menarik tidak lagi memerlukan library kompleks. Artikel ini akan memandu Anda langkah demi langkah membuat heatmap sederhana dari nol menggunakan vanilla JavaScript dan Canvas API, dengan prinsip yang tetap relevan untuk tahun-tahun mendatang.

Cara Membuat Heatmap Sederhana dengan JavaScript

Mengapa Membuat Heatmap dari Nol di Tahun 2025?

Meskipun banyak library hebat seperti Leaflet.heat atau WebGL-based solutions tersedia, memahami cara membangun heatmap dasar memberikan kontrol penuh, ukuran bundle yang minimal (critical untuk performa web), dan wawasan mendalam tentang bagaimana data diubah menjadi visual. Tren pengembangan web 2025 sangat menekankan pada performa, privasi data on-device, dan customizability. Dengan solusi buatan sendiri, Anda memenuhi semua itu tanpa ketergantungan eksternal.

Prinsip Dasar di Balik Heatmap

Secara konseptual, heatmap adalah representasi visual dari kepadatan titik data pada bidang dua dimensi. Setiap titik data memberikan "panas" (heat) yang memudar seiring jarak. Hasil superimposisi (tumpang-tindih) dari semua titik inilah yang menciptakan gradien warna yang kita lihat. Proses teknisnya melibatkan tiga tahap utama: Pembuatan Peta Kepadatan (Density Grid), Penerapan Kernel Gaussian (atau fungsi radiasi lainnya), dan Pemetaan Warna (Color Mapping).

Alat dan Persiapan

Kita hanya memerlukan editor kode dan browser modern. Struktur proyek kita sangat sederhana:

  • File HTML sebagai wadah.
  • File JavaScript untuk logika heatmap.
  • Canvas API untuk menggambar.
  • Dataset titik koordinat (kita akan gunakan data dummy).

Langkah 1: Membuat Kerangka HTML dan Canvas

Pertama, kita siapkan wadah visualisasi. Elemen <canvas> akan menjadi panggung tempat kita melukis heatmap.

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Heatmap JavaScript Sederhana</title>
    <style>
        body { margin: 40px; font-family: sans-serif; }
        #container { text-align: center; }
        #heatmapCanvas {
            border: 1px solid #ccc;
            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
            background-color: #f9f9f9;
        }
        #info { margin-top: 15px; color: #555; }
    </style>
</head>
<body>
    <div id="container">
        <h1>Visualisasi Heatmap Sederhana</h1>
        <canvas id="heatmapCanvas" width="800" height="600">
            Browser Anda tidak mendukung elemen Canvas.
        </canvas>
        <p id="info">Klik atau seret pada canvas untuk menambah titik data 'panas'.</p>
    </div>
    <script src="heatmap.js"></script>
</body>
</html>

Langkah 2: Logika Inti Heatmap dalam JavaScript

Buat file heatmap.js. Logika inti akan kita bagi menjadi beberapa fungsi modular.

2.1. Inisialisasi dan Data Contoh

Kita mulai dengan mengatur konteks canvas dan menyiapkan data titik. Di tahun 2025, data bisa berasal dari API, sensor, atau input interaktif pengguna.

// heatmap.js
const canvas = document.getElementById('heatmapCanvas');
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;

// Simpan titik data sebagai array of objects {x, y, intensity}
let dataPoints = [];

// Generate data acak untuk contoh awal
function generateSampleData(count) {
    dataPoints = [];
    for (let i = 0; i < count; i++) {
        dataPoints.push({
            x: Math.random() * width,
            y: Math.random() * height,
            intensity: 0.5 + Math.random() * 0.5 // Intensitas antara 0.5 dan 1
        });
    }
}
// Generate 100 titik contoh
generateSampleData(100);

2.2. Membangun Grid Kepadatan

Kita membuat grid 2D (array 2 dimensi) yang mewakili kanvas. Setiap sel grid akan menyimpan nilai akumulasi "panas". Resolusi grid bisa lebih rendah dari kanvas untuk performa (teknik downsampling).

const gridResolution = 4; // Pixel per sel grid. Lebih besar = lebih cepat, kurang detail.
const gridWidth = Math.ceil(width / gridResolution);
const gridHeight = Math.ceil(height / gridResolution);

function createDensityGrid() {
    // Inisialisasi grid dengan nilai 0
    const grid = new Array(gridHeight);
    for (let y = 0; y < gridHeight; y++) {
        grid[y] = new Array(gridWidth).fill(0);
    }

    // Untuk setiap titik data, sebarkan 'panas'-nya ke grid terdekat
    const radius = 15; // Radius penyebaran panas dalam pixel
    const gridRadius = Math.ceil(radius / gridResolution);

    dataPoints.forEach(point => {
        const gridX = Math.floor(point.x / gridResolution);
        const gridY = Math.floor(point.y / gridResolution);
        const intensity = point.intensity;

        // Sebarkan ke sel-sel di sekitar titik pusat
        for (let dy = -gridRadius; dy <= gridRadius; dy++) {
            for (let dx = -gridRadius; dx <= gridRadius; dx++) {
                const targetX = gridX + dx;
                const targetY = gridY + dy;

                // Pastikan target dalam batas grid
                if (targetX >= 0 && targetX < gridWidth && targetY >= 0 && targetY < gridHeight) {
                    // Hitung jarak (dalam satuan grid) dari titik pusat
                    const distance = Math.sqrt(dx * dx + dy * dy);
                    // Fungsi Gaussian sederhana: panas berkurang seiring jarak
                    if (distance <= gridRadius) {
                        const falloff = Math.exp(-distance * distance / (gridRadius * gridRadius / 2));
                        grid[targetY][targetX] += intensity * falloff;
                    }
                }
            }
        }
    });
    return grid;
}

2.3. Pemetaan Warna (Color Gradient)

Nilai numerik di grid harus dikonversi ke warna. Kita buat fungsi yang menginterpolasi antara beberapa warna untuk membuat gradient yang smooth.

function getColorForValue(value, maxValue) {
    // Normalisasi nilai antara 0 dan 1
    const normalized = Math.min(value / maxValue, 1.0);

    // Definisikan tahapan gradient (RGB). Bisa disesuaikan!
    // Dari biru (dingin) -> cyan -> hijau -> kuning -> merah (panas)
    const colors = [
        {r: 0, g: 0, b: 255},   // Biru
        {r: 0, g: 255, b: 255}, // Cyan
        {r: 0, g: 255, b: 0},   // Hijau
        {r: 255, g: 255, b: 0}, // Kuning
        {r: 255, g: 0, b: 0}    // Merah
    ];

    const segment = normalized * (colors.length - 1);
    const index = Math.floor(segment);
    const frac = segment - index;

    if (index >= colors.length - 1) return colors[colors.length - 1];

    // Interpolasi linear antara dua warna
    const c1 = colors[index];
    const c2 = colors[index + 1];
    const r = Math.round(c1.r + (c2.r - c1.r) * frac);
    const g = Math.round(c1.g + (c2.g - c1.g) * frac);
    const b = Math.round(c1.b + (c2.b - c1.b) * frac);

    return `rgb(${r}, ${g}, ${b})`;
}

2.4. Fungsi Render untuk Menggambar Heatmap

Sekarang gabungkan semua bagian: buat grid, cari nilai maksimum untuk normalisasi, dan gambar setiap sel grid sebagai persegi panjang berwarna.

function renderHeatmap() {
    // 1. Buang canvas
    ctx.clearRect(0, 0, width, height);

    // 2. Buat grid kepadatan
    const grid = createDensityGrid();

    // 3. Cari nilai maksimum di grid untuk normalisasi
    let maxDensity = 0.01; // Hindari pembagian dengan nol
    for (let y = 0; y < gridHeight; y++) {
        for (let x = 0; x < gridWidth; x++) {
            maxDensity = Math.max(maxDensity, grid[y][x]);
        }
    }

    // 4. Gambar setiap sel grid
    for (let y = 0; y < gridHeight; y++) {
        for (let x = 0; x < gridWidth; x++) {
            const density = grid[y][x];
            if (density > 0) {
                ctx.fillStyle = getColorForValue(density, maxDensity);
                ctx.fillRect(
                    x * gridResolution,
                    y * gridResolution,
                    gridResolution, // Lebar sel
                    gridResolution  // Tinggi sel
                );
            }
        }
    }

    // (Opsional) Gambar titik data asli untuk referensi
    ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
    dataPoints.forEach(point => {
        ctx.beginPath();
        ctx.arc(point.x, point.y, 3, 0, Math.PI * 2);
        ctx.fill();
    });
}

Langkah 3: Menambahkan Interaktivitas

Heatmap yang statis sudah bagus, tetapi interaktivitas adalah standar di 2025. Mari tambahkan kemampuan untuk menambah titik data dengan klik mouse.

// Event listener untuk klik dan drag pada canvas
let isDrawing = false;
canvas.addEventListener('mousedown', (e) => {
    isDrawing = true;
    addDataPoint(e);
});
canvas.addEventListener('mousemove', (e) => {
    if (isDrawing) {
        addDataPoint(e);
    }
});
canvas.addEventListener('mouseup', () => { isDrawing = false; });
canvas.addEventListener('mouseleave', () => { isDrawing = false; });

function addDataPoint(event) {
    const rect = canvas.getBoundingClientRect();
    const x = event.clientX - rect.left;
    const y = event.clientY - rect.top;

    // Tambahkan titik dengan intensitas acak
    dataPoints.push({ x, y, intensity: 0.7 + Math.random() * 0.3 });
    // Render ulang heatmap
    renderHeatmap();
}

// Render pertama kali
renderHeatmap();

Optimasi dan Tren Masa Depan (2025 dan Seterusnya)

Implementasi dasar ini sudah fungsional. Untuk proyek produksi di era sekarang, pertimbangkan optimasi berikut:

  • Web Workers: Proses perhitungan grid yang berat dapat dipindahkan ke thread terpisah agar UI tetap responsif.
  • WebGL (via Framebuffer & Shaders): Untuk dataset besar (puluhan ribu titik), rendering dengan fragment shader di WebGL akan memberikan performa yang jauh lebih unggul. Ini adalah tren dominan untuk visualisasi data kompleks.
  • Progressive Rendering & Level of Detail (LoD): Render heatmap dengan resolusi rendah terlebih dahulu saat zoom out, lalu tingkatkan detail saat zoom in.
  • Integrasi dengan Library Peta: Heatmap spasial geografis dapat diintegrasikan dengan proyeksi peta (misalnya, menggunakan Canvas pada library seperti MapLibre GL JS).
  • Accessibility (A11Y): Sediakan alternatif tekstual atau tabel data untuk pengguna screen reader, karena informasi dalam heatmap bersifat visual murni.

Kesimpulan

Membuat heatmap sederhana dengan JavaScript murni adalah latihan yang sangat berharga untuk memahami aliran data menjadi visual. Dengan menguasai konsep grid density, fungsi falloff, dan color mapping, Anda memiliki fondasi untuk mengembangkan visualisasi yang lebih kompleks atau mengintegrasikan logika ini ke dalam framework modern seperti React, Vue, atau Svelte. Di tahun 2025, di mana customisasi dan performa adalah raja, kemampuan untuk membangun solusi visualisasi yang ringan dan tepat guna dari prinsip pertama akan menjadi aset berharga bagi developer front-end dan data engineer. Cobalah eksperimen dengan fungsi falloff yang berbeda, gradient warna, atau sumber data real-time untuk melihat heatmap Anda benar-benar hidup.

Langkah Selanjutnya: Coba ganti fungsi Gaussian dengan fungsi linear atau eksponensial, implementasi zoom/pan pada canvas, atau ambil data titik dari API publik untuk membuat visualisasi yang dinamis dan meaningful. Selamat berkoding!


Subscribe
Notify of
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
share
facebook
©MarketingAmpuh.com. Jogja-Indonesia.