Ostre renderowanie płótna na ekranach o wysokiej rozdzielczości i ekranach Retina
Dlaczego grafika na płótnie wygląda rozmazana na ekranach Retina i ekranach o wysokiej rozdzielczości oraz jak to naprawić za pomocą urządzeniaPixelRatio, w tym obsługa zmiany rozmiaru i ograniczenia wydajności.
Rdzeń na wynos
Płótno wygląda na rozmyte na ekranach o dużej gęstości, gdy jego rozmiar w pikselach odpowiada rozmiarowi CSS, ponieważ każdy piksel płótna jest rozciągnięty na kilka fizycznych pikseli. Napraw to, ustawiając szerokość i wysokość płótna na rozmiar CSS pomnożony przez window.devicePixelRatio, zachowując niezmieniony rozmiar CSS i skalując kontekst rysunku o ten sam współczynnik. Zaktualizuj go, zmieniając rozmiar i powiększając.
Dlaczego płótna się rozmazują
MDN opisuje DevicePixelRatio jako stosunek pikseli fizycznych do pikseli CSS na bieżącym wyświetlaczu: 1 na klasycznym ekranie 96 DPI i 2 na typowych ekranach HiDPI lub Retina. Płótno ma swój własny bufor pikseli. Jeśli ten bufor ma wymiary 300 na 150 pikseli, ale jest wyświetlany w rozdzielczości 300 na 150 pikseli CSS na ekranie ze współczynnikiem 2, przeglądarka rozciąga go na fizyczne piksele 600 na 300, co wygląda miękko.
Poprawka
Zalecanym podejściem MDN jest użycie DevicePixelRatio w celu dodania dodatkowej gęstości pikseli: ustaw bufor płótna na rozmiar CSS pomnożony przez współczynnik, zachowaj ten sam rozmiar CSS i skaluj kontekst 2D, aby Twój kod rysunkowy mógł nadal używać pikseli CSS. Zobacz poniższy kod.
Uchwyt zmienia rozmiar i powiększanie
Współczynnik zmienia się, gdy okno zostanie przeniesione na inny monitor lub użytkownik powiększy stronę. Przelicz ponownie przy zmianie rozmiaru i nasłuchuj zmian w zapytaniu o media rozdzielczości, aby móc ponownie narysować z nową gęstością. ResizeObserver na elemencie canvas to niezawodny sposób na synchronizację rozmiaru z układem.
Granice wydajności
Podwojenie współczynnika powoduje czterokrotne zwiększenie liczby pikseli do narysowania. Na bardzo dużych płótnach lub urządzeniach z niższej półki ogranicz współczynnik, na przykład do 2, i zmierz czas trwania klatek. Środowiska wykonawcze animacji rysujące na płótnie, takie jak moduł renderujący płótno Lottie, Rive i ExodeUI, obsługują to skalowanie za Ciebie, ale obowiązują te same kompromisy.
Fragment wdrożenia produkcyjnego
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
}Klucz Principles do zapamiętania
- DevicePixelRatio = fizyczne piksele na piksel CSS (2 na typowych ekranach Retina)
- Ustaw canvas.width/height na CSS rozmiar × stosunek; zachowaj rozmiar CSS
- Skaluj kontekst, aby kod rysunkowy korzystał z pikseli CSS
- Oblicz ponownie przy zmianie rozmiaru, monitorowaniu zmiany i powiększeniu
- Ogranicz proporcje na dużych płótnach, aby uzyskać wydajność
Często zadawane pytania
Źródła
- Window: devicePixelRatio property — Dokumenty internetowe MDN