Empat chapter sebelumnya membekali kita dua teknologi gambar: SVG menyimpan objek di DOM, canvas melukis piksel lewat JavaScript. Keduanya tidak saling menggantikan; masing-masing unggul pada pekerjaan berbeda. Chapter ini merangkum keputusannya: matriks cepat, tabel perbandingan, keputusan inline versus file, dan studi kasus satu visual dua cara.

Matriks Keputusan Cepat #

Kebutuhan paling sering muncul dapat dipetakan seketika:

  • Ikon, logo, dan diagram statis: SVG, karena ringan, tajam, dan dapat di-style;
  • Gambar yang harus tetap jernih saat di-zoom: SVG, karena browser merender ulang dari deskripsi vektor;
  • Game 2D dan simulasi partikel: canvas, karena gambar ulang tiap frame adalah pola bawaannya;
  • Puluhan ribu titik data atau edit tingkat piksel: canvas, karena ribuan objek SVG menumpuk DOM;
  • Chart data: keduanya mungkin; SVG untuk chart kecil yang perlu aksesibel dan interaktif, canvas untuk dataset besar yang hanya dilihat.

Aturan praktisnya: makin banyak objek yang harus tetap bernyawa sebagai elemen, makin cocok SVG; makin banyak piksel yang dirender tiap frame, makin cocok canvas.

Tabel Perbandingan Lengkap #

AspekSVGCanvas
RepresentasiObjek vektor yang tersimpan di DOMPiksel hasil gambar
Cara menggambarMarkup XML yang dideklarasikanPerintah JavaScript yang dieksekusi
Akses per bentukSetiap bentuk bisa dipilih dan diubahTidak ada objek; satu elemen canvas saja
AksesibilitasNama lewat title, desc, roleTerbatas: fallback content dan aria-label
AnimasiCSS, SMIL, atau JS mengubah atributLoop gambar ulang tiap frame
Interaksi per objekEvent pada setiap bentukHit-testing manual di koordinat kursor
Performa banyak objekMenurun saat DOM menumpukStabil bila jumlah gambar per frame terkendali
Ketajaman saat zoomTetap tajamMengikuti resolusi piksel kanvas

Dua baris terakhir sering menentukan. Diagram organisasi dengan ratusan kotak yang perlu diklik tetap nyaman di SVG; peta bintang dengan puluhan ribu titik hanya realistis di canvas.

Keputusan Ulang: Inline atau img #

Setelah memilih SVG, keputusan kedua perlu diulang dengan kerangka ringkas:

  • Perlu styling CSS halaman atau akses DOM: inline SVG;
  • Dipakai lintas halaman dan menguntungkan cache: file SVG lewat img;
  • Berasal dari pihak ketiga: lewat img, karena script di dalamnya tidak dijalankan.

Studi Kasus: Satu Visual, Dua Cara #

Ring progres menampakkan karakter keduanya dalam satu visual: lingkaran abu penuh, busur hijau 70% keliling, dan teks persentase di tengah. File berikut menggambar visual yang sama dua kali:

<!doctype html>
<html lang="id">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Ring Progres: SVG dan Canvas</title>
</head>
<body>
  <h1>Progres Unduhan: 70%</h1>
  <p>Visual yang sama, digambar dengan dua teknologi berbeda.</p>
  <figure>
    <svg viewBox="0 0 120 120" width="120" height="120" role="img" aria-label="Ring progres 70 persen, versi SVG">
      <circle cx="60" cy="60" r="45" fill="none" stroke="#f3f4f6" stroke-width="12"/>
      <circle cx="60" cy="60" r="45" fill="none" stroke="#1a6b54" stroke-width="12" stroke-linecap="round" stroke-dasharray="198 283" transform="rotate(-90 60 60)"/>
      <text x="60" y="67" text-anchor="middle" font-size="20" font-weight="bold" fill="#1f2937">70%</text>
    </svg>
    <figcaption>Versi SVG: dua lingkaran dan satu teks di dalam DOM.</figcaption>
  </figure>
  <figure>
    <canvas id="ring" width="120" height="120" role="img" aria-label="Ring progres 70 persen, versi canvas">
      Ring hijau menunjukkan progres unduhan 70 persen.
    </canvas>
    <figcaption>Versi canvas: digambar oleh JavaScript ke piksel.</figcaption>
  </figure>
  <script src="studi-dua-cara.js" defer></script>
</body>
</html>
javascript studi-dua-cara.js
function gambarRing() {
  const kanvas = document.querySelector("#ring");
  const konteks = kanvas.getContext("2d");

  konteks.lineWidth = 12;
  konteks.strokeStyle = "#f3f4f6";
  konteks.beginPath();
  konteks.arc(60, 60, 45, 0, Math.PI * 2);
  konteks.stroke();

  konteks.strokeStyle = "#1a6b54";
  konteks.beginPath();
  konteks.arc(60, 60, 45, -Math.PI / 2, Math.PI * 2 * 0.7 - Math.PI / 2);
  konteks.stroke();

  konteks.fillStyle = "#1f2937";
  konteks.font = "bold 20px sans-serif";
  konteks.textAlign = "center";
  konteks.fillText("70%", 60, 67);
}

gambarRing();

Output yang Dihasilkan:

text
Jendela browser menampilkan dua gambar identik 120x120:
  - Ring abu terang penuh
  - Busur hijau 70% keliling, mulai dari titik pukul 12 searah jarum jam
  - Teks "70%" gelap di tengah ring
  - Bagian SVG terdiri atas elemen DOM; bagian canvas berupa piksel

Mari kita bedah kuncinya:

  1. Baris 13-14 pada HTML (dua circle): lingkaran pertama menjadi jalur abu penuh; lingkaran kedua memakai stroke-dasharray="198 283", yaitu busur hijau 198 dari keliling sekitar 283 (2 x pi x 45), tepat 70%.
  2. transform="rotate(-90 60 60)": memutar titik awal busur ke atas; tanpa rotasi, busur mulai di pukul 3.
  3. Baris 15 pada HTML (text): persentase menjadi elemen DOM, terbaca pembaca layar.
  4. Baris 5-9 pada JS (ring abu): lineWidth dan strokeStyle disetel, lalu lingkaran penuh distroke; urutannya menggantikan atribut deklaratif milik SVG.
  5. Baris 11-14 pada JS (busur hijau): arc digambar dari sudut -Math.PI / 2 (titik pukul 12) sejauh 70% dari 2 x pi radian; hitungan trigonometri ini harga yang dibayar immediate mode.
  6. Baris 16-19 pada JS (fillText): teks persentase dilukis ke piksel dengan textAlign tengah.

Bisnis berubah menjadi 85%: versi SVG cukup mengganti stroke-dasharray menjadi “240 283” dan teksnya; versi canvas harus mengosongkan kanvas lalu menggambar ulang dari awal. Perbedaan kerja ini cermin retained mode versus immediate mode.

Peta Lanjutan #

Jalur pendalaman terbuka di tiga arah. Animasi SVG dapat lewat CSS, SMIL (animasi berbasis markup di dalam file), atau JavaScript yang mengubah atribut; pustaka seperti D3 menjadikan SVG basis chart interaktif. Sisi canvas berlanjut ke loop requestAnimationFrame, konteks WebGL untuk grafis tiga dimensi, dan manipulasi piksel lewat getImageData. Materi HTML tinggal satu section lagi: Browser dan Web Platform membahas mesin yang mengerjakan markup ini, dari parsing hingga DOM.

Referensi resmi untuk pendalaman:

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

studi-dua-cara.html

Unduh berkas
html
<!doctype html>
<html lang="id">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Ring Progres: SVG dan Canvas</title>
</head>
<body>
  <h1>Progres Unduhan: 70%</h1>
  <p>Visual yang sama, digambar dengan dua teknologi berbeda.</p>
  <figure>
    <svg viewBox="0 0 120 120" width="120" height="120" role="img" aria-label="Ring progres 70 persen, versi SVG">
      <circle cx="60" cy="60" r="45" fill="none" stroke="#f3f4f6" stroke-width="12"/>
      <circle cx="60" cy="60" r="45" fill="none" stroke="#1a6b54" stroke-width="12" stroke-linecap="round" stroke-dasharray="198 283" transform="rotate(-90 60 60)"/>
      <text x="60" y="67" text-anchor="middle" font-size="20" font-weight="bold" fill="#1f2937">70%</text>
    </svg>
    <figcaption>Versi SVG: dua lingkaran dan satu teks di dalam DOM.</figcaption>
  </figure>
  <figure>
    <canvas id="ring" width="120" height="120" role="img" aria-label="Ring progres 70 persen, versi canvas">
      Ring hijau menunjukkan progres unduhan 70 persen.
    </canvas>
    <figcaption>Versi canvas: digambar oleh JavaScript ke piksel.</figcaption>
  </figure>
  <script src="studi-dua-cara.js" defer></script>
</body>
</html>

studi-dua-cara.js

Unduh berkas
javascript
function gambarRing() {
  const kanvas = document.querySelector("#ring");
  const konteks = kanvas.getContext("2d");

  konteks.lineWidth = 12;
  konteks.strokeStyle = "#f3f4f6";
  konteks.beginPath();
  konteks.arc(60, 60, 45, 0, Math.PI * 2);
  konteks.stroke();

  konteks.strokeStyle = "#1a6b54";
  konteks.beginPath();
  konteks.arc(60, 60, 45, -Math.PI / 2, Math.PI * 2 * 0.7 - Math.PI / 2);
  konteks.stroke();

  konteks.fillStyle = "#1f2937";
  konteks.font = "bold 20px sans-serif";
  konteks.textAlign = "center";
  konteks.fillText("70%", 60, 67);
}

gambarRing();