Rendering Kanvas Tajam pada Layar DPI Tinggi dan Retina
Mengapa grafik kanvas terlihat buram di Retina dan layar DPI tinggi, dan cara memperbaikinya dengan devicePixelRatio, termasuk penanganan perubahan ukuran dan batas performa.
Pengambilan Inti
Kanvas terlihat buram pada layar dengan kepadatan tinggi ketika ukuran pikselnya sesuai dengan ukuran CSS, karena setiap piksel kanvas direntangkan pada beberapa piksel fisik. Perbaiki dengan mengatur lebar dan tinggi kanvas ke ukuran CSS dikalikan dengan window.devicePixelRatio, menjaga ukuran CSS tidak berubah, dan menskalakan konteks gambar dengan rasio yang sama. Perbarui saat mengubah ukuran dan memperbesar.
Mengapa kanvas kabur
MDN mendeskripsikan devicePixelRatio sebagai rasio piksel fisik terhadap piksel CSS pada tampilan saat ini: 1 pada layar klasik 96 DPI dan 2 pada layar HiDPI atau Retina biasa. Kanvas memiliki buffer pikselnya sendiri. Jika buffer tersebut berukuran 300 x 150 piksel tetapi ditampilkan pada 300 x 150 CSS piksel pada layar dengan rasio 2, browser akan merentangkannya menjadi lebih dari 600 x 300 piksel fisik, yang terlihat lembut.
Cara mengatasinya
Pendekatan yang disarankan MDN adalah menggunakan devicePixelRatio untuk menambahkan kerapatan piksel ekstra: atur buffer kanvas ke ukuran CSS dikali rasio, pertahankan ukuran CSS tetap sama, dan skalakan konteks 2D sehingga kode gambar Anda dapat tetap menggunakan piksel CSS. Lihat kode di bawah ini.
Menangani pengubahan ukuran dan zoom
Rasio berubah ketika jendela berpindah ke monitor lain atau pengguna memperbesar halaman. Hitung ulang saat mengubah ukuran, dan dengarkan perubahan pada kueri media resolusi sehingga Anda dapat menggambar ulang pada kepadatan baru. ResizeObserver pada elemen kanvas adalah cara yang dapat diandalkan untuk menjaga ukuran tetap sinkron dengan tata letak.
Batasan kinerja
Menggandakan rasio akan melipatgandakan piksel yang akan digambar. Pada kanvas yang sangat besar atau perangkat kelas bawah, batasi rasionya, misalnya 2, dan ukur waktu frame. Runtime animasi yang ditarik ke kanvas, seperti penyaji kanvas Lottie, Rive dan ExodeUI, menangani penskalaan ini untuk Anda, namun trade-off yang sama berlaku.
Cuplikan Implementasi Produksi
function setupCanvas(canvas) {
const ctx = canvas.getContext('2d');
function resize() {
const ratio = Math.min(window.devicePixelRatio || 1, 2); // cap for performance
const { width, height } = canvas.getBoundingClientRect(); // CSS size
canvas.width = Math.round(width * ratio); // backing store in device pixels
canvas.height = Math.round(height * ratio);
ctx.setTransform(ratio, 0, 0, ratio, 0, 0); // draw in CSS pixels
draw(ctx, width, height);
}
new ResizeObserver(resize).observe(canvas);
resize();
}
function draw(ctx, width, height) {
ctx.clearRect(0, 0, width, height);
ctx.lineWidth = 1;
ctx.strokeRect(0.5, 0.5, width - 1, height - 1); // crisp 1px border
}Kunci Principles untuk Diingat
- devicePixelRatio = piksel fisik per piksel CSS (2 pada layar Retina biasa)
- Atur canvas.width/height ke ukuran × rasio CSS; pertahankan ukuran CSS
- Skalakan konteksnya sehingga kode gambar menggunakan piksel CSS
- Hitung ulang dalam mengubah ukuran, memantau perubahan, dan memperbesar
- Batasi rasio pada kanvas besar untuk performa
Pertanyaan yang Sering Diajukan
Sumber
- Window: devicePixelRatio property — Dokumen Web MDN