Selalu jadi Anda, Canvas2D

Aaron Krajeski
Aaron Krajeski

Di dunia shader, mesh, dan filter, Canvas2D mungkin tidak membuat Anda tertarik. Namun, seharusnya tidak demikian. 30–40% halaman web memiliki elemen <canvas> dan 98% dari semua kanvas menggunakan konteks rendering Canvas2D. Ada Canvas2D di mobil, di kulkas, dan di luar angkasa (benar-benar).

Memang, API ini sedikit ketinggalan zaman dalam hal gambar 2D canggih. Untungnya, kami telah bekerja keras menerapkan fitur baru di Canvas2D untuk mengejar CSS, menyederhanakan ergonomi, dan meningkatkan performa.

Bagian 1: memahami CSS

CSS memiliki beberapa perintah gambar yang sangat tidak ada di Canvas2D. Dengan API baru, kami telah menambahkan beberapa fitur yang paling banyak diminta:

Persegi panjang bulat

Persegi panjang membulat: fondasi internet, komputasi, bahkan peradaban.

Sejujurnya, persegi panjang membulat sangat berguna: sebagai tombol, balon chat, thumbnail, balon ucapan, dan sebagainya. Anda selalu dapat membuat persegi panjang membulat di Canvas2D, tetapi sedikit berantakan:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'magenta';

const top = 10;
const left = 10;
const width = 200;
const height = 100;
const radius = 20;

ctx.beginPath();
ctx.moveTo(left + radius, top);
ctx.lineTo(left + width - radius, top);
ctx.arcTo(left + width, top, left + width, top + radius, radius);
ctx.lineTo(left + width, top + height - radius);
ctx.arcTo(left + width, top + height, left + width - radius, top + height, radius);
ctx.lineTo(left + radius, top + height);
ctx.arcTo(left, top + height, left, top + height - radius, radius);
ctx.lineTo(left, top + radius);
ctx.arcTo(left, top, left + radius, top, radius);
ctx.stroke();

Semua ini diperlukan untuk persegi panjang membulat sederhana dan sederhana:

Persegi panjang membulat.

Dengan API baru, ada metode roundRect().

ctx.roundRect(upper, left, width, height, borderRadius);

Jadi, kode di atas dapat sepenuhnya diganti dengan:

ctx.roundRect(10, 10, 200, 100, 20);

Metode ctx.roundRect() juga menggunakan array untuk argumen borderRadius hingga empat angka. Radius ini mengontrol empat sudut persegi panjang membulat dengan cara yang sama seperti untuk CSS. Contoh:

ctx.roundRect(10, 10, 200, 100, [15, 50, 30]);

Lihat demo untuk mencobanya.

Gradien Konikal

Anda telah melihat gradien linear:

const gradient = ctx.createLinearGradient(0, 0, 200, 100);
gradient.addColorStop(0, 'blue');
gradient.addColorStop(0.5, 'magenta');
gradient.addColorStop(1, 'white');
ctx.fillStyle = gradient;
ctx.fillRect(10, 10, 200, 100);

Gradien linear.

Gradien radial:

const radialGradient = ctx.createRadialGradient(150, 75, 10, 150, 75, 70);
radialGradient.addColorStop(0, 'white');
radialGradient.addColorStop(0.5, 'magenta');
radialGradient.addColorStop(1, 'lightblue');

ctx.fillStyle = radialGradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);

Gradien radial.

Namun, bagaimana dengan gradien kerucut yang bagus?

const grad = ctx.createConicGradient(0, 100, 100);

grad.addColorStop(0, 'red');
grad.addColorStop(0.25, 'orange');
grad.addColorStop(0.5, 'yellow');
grad.addColorStop(0.75, 'green');
grad.addColorStop(1, 'blue');

ctx.fillStyle = grad;
ctx.fillRect(0, 0, 200, 200);

Gradien kerucut.

Pengubah teks

Kemampuan rendering teks Canvas2D sangat tertinggal. Chrome telah menambahkan beberapa atribut baru ke rendering teks Canvas2D:

Semua atribut ini cocok dengan pasangan CSS-nya dengan nama yang sama.

Bagian 2: penyesuaian ergonomis

Sebelumnya, beberapa hal dengan Canvas2D dapat dilakukan, tetapi tidak perlu rumit untuk diterapkan. Berikut beberapa peningkatan kualitas hidup untuk developer JavaScript yang ingin menggunakan Canvas2D:

Reset konteks

Untuk menjelaskan cara menghapus kanvas, saya telah menulis fungsi kecil yang lucu untuk menggambar pola retro:

draw90sPattern();

Pola retro segitiga dan persegi.

Bagus! Setelah selesai dengan pola tersebut, saya ingin menghapus kanvas dan menggambar sesuatu yang lain. Tunggu, bagaimana cara menghapus kanvas lagi? Oh, iya! ctx.clearRect(), tentu saja.

ctx.clearRect(0, 0, canvas.width, canvas.height);

Hmm… tidak berhasil. Oh, iya! Saya harus mereset transformasi terlebih dahulu:

ctx.resetTransform();
ctx.clearRect(0, 0, canvas.width, canvas.height);
Kanvas kosong.

Sempurna! Kanvas kosong yang bagus. Sekarang, mari kita mulai menggambar garis horizontal yang bagus:

ctx.moveTo(10, 10);
ctx.lineTo(canvas.width, 10);
ctx.stroke();

Garis horizontal dan diagonal.

Grrrr! Jawaban Anda salah. 😡 Apa fungsi baris tambahan tersebut? Selain itu, mengapa warnanya merah muda? Baik, mari kita periksa StackOverflow.

canvas.width = canvas.width;

Mengapa hal ini sangat konyol? Mengapa hal ini sangat sulit?

Tidak lagi. Dengan API baru, kami memiliki fitur yang sederhana, elegan, dan menarik:

ctx.reset();

Maaf, prosesnya memerlukan waktu lama.

Filter

Filter SVG adalah dunia tersendiri. Jika Anda baru mengenalnya, sebaiknya baca The Art Of SVG Filters And Why It Is Awesome, yang menunjukkan beberapa potensinya yang luar biasa.

Filter gaya SVG sudah tersedia untuk Canvas2D. Anda hanya perlu bersedia meneruskan filter sebagai URL yang mengarah ke elemen filter SVG lain di halaman:

<svg>
  <defs>
    <filter id="svgFilter">
      <feGaussianBlur in="SourceGraphic" stdDeviation="5" />
      <feConvolveMatrix kernelMatrix="-3 0 0 0 0.5 0 0 0 3" />
      <feColorMatrix type="hueRotate" values="90" />
    </filter>
  </defs>
</svg>
const canvas = document.createElement('canvas');
canvas.width = 500;
canvas.height = 400;
const ctx = canvas.getContext('2d');
document.body.appendChild(canvas);

ctx.filter = "url('#svgFilter')";
draw90sPattern(ctx);

Hal ini cukup mengacaukan pola kita:

Pola retro dengan efek blur diterapkan.

Namun, bagaimana jika Anda ingin melakukan hal di atas, tetapi tetap dalam JavaScript dan tidak perlu repot dengan string? Dengan API baru, hal ini sangat mungkin.

ctx.filter = new CanvasFilter([
  { filter: 'gaussianBlur', stdDeviation: 5 },
  {
    filter: 'convolveMatrix',
    kernelMatrix: [
      [-3, 0, 0],
      [0, 0.5, 0],
      [0, 0, 3],
    ],
  },
  { filter: 'colorMatrix', type: 'hueRotate', values: 90 },
]);

Sangat mudah. Coba dan mainkan parameter dalam demo di sini.

Bagian 3: peningkatan performa

Dengan Canvas2D API Baru, kami juga ingin meningkatkan performa jika memungkinkan. Kami menambahkan beberapa fitur untuk memberi developer kontrol yang lebih terperinci atas situs mereka dan memungkinkan kecepatan frame yang paling lancar:

Akan sering dibaca

Gunakan getImageData() untuk membaca data piksel kembali dari kanvas. Proses ini bisa sangat lambat. API baru ini memberi Anda cara untuk menandai kanvas secara eksplisit untuk dibaca kembali (misalnya, untuk efek generatif). Hal ini memungkinkan Anda mengoptimalkan berbagai hal di balik layar dan menjaga kanvas tetap cepat untuk berbagai kasus penggunaan yang lebih besar. Fitur ini telah ada di Firefox selama beberapa waktu dan akhirnya kami menjadikannya bagian dari spesifikasi kanvas.

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });

Hilangnya konteks

Mari kita buat tab yang sedih menjadi bahagia lagi. Jika klien kehabisan memori GPU atau bencana lain terjadi pada kanvas, Anda kini dapat menerima callback dan menggambar ulang sesuai kebutuhan:

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');

canvas.addEventListener('contextlost', onContextLost);
canvas.addEventListener('contextrestored', redraw);

Jika Anda ingin membaca lebih lanjut tentang konteks dan kehilangan kanvas, WHATWG memiliki penjelasan yang bagus di wiki-nya.

Kesimpulan

Baik Anda baru menggunakan Canvas2D, sudah menggunakannya selama bertahun-tahun, atau telah menghindari penggunaannya selama bertahun-tahun, kami ingin menyarankan Anda untuk melihat kembali canvas. Ini adalah API-next-door yang sudah ada selama ini.