Sharp Canvas Rendering на High-DPI и Retina екрани
Защо графиките на платното изглеждат замъглени на Retina и екрани с висок DPI и как да го коригирате с devicePixelRatio, включително обработка на преоразмеряването и ограничения на производителността.
Ядро за вкъщи
Едно платно изглежда размазано на екрани с висока плътност, когато неговият размер на пиксела съвпада с неговия CSS размер, тъй като всеки пиксел на платното е опънат върху няколко физически пиксела. Поправете го, като зададете ширината и височината на платното на размера CSS, умножен по window.devicePixelRatio, като запазите размера CSS непроменен и мащабирате контекста на чертежа със същото съотношение. Актуализирайте го при преоразмеряване и мащабиране.
Защо платната се размазват
MDN описва devicePixelRatio като съотношението на физическите пиксели към CSS пикселите на текущия дисплей: 1 на класически 96 DPI екран и 2 на типични HiDPI или Retina екрани. Платното има свой собствен пикселен буфер. Ако този буфер е 300 на 150 пиксела, но се показва на 300 на 150 CSS пиксела на екран със съотношение 2, браузърът го разтяга над 600 на 300 физически пиксела, което изглежда меко.
Поправката
Препоръчителният подход на MDN е да използвате devicePixelRatio, за да добавите допълнителна плътност на пикселите: задайте буфера на платното на CSS размер, умножен по съотношението, запазете CSS размера същия и мащабирайте 2D контекста, така че вашият код за рисуване да може да продължи да използва CSS пиксели. Вижте кода по-долу.
Управление на преоразмеряването и мащабирането
Съотношението се променя, когато прозорец се премести на друг монитор или потребителят мащабира страницата. Преизчислете при преоразмеряване и слушайте за промени в медийната заявка за резолюция, за да можете да преначертаете с новата плътност. ResizeObserver на елемента canvas е надежден начин да поддържате размера в синхрон с оформлението.
Граници на производителността
Удвояването на съотношението учетворява пикселите за рисуване. На много големи платна или устройства от нисък клас, ограничете съотношението, например на 2, и измерете кадровите времена. Среди за изпълнение на анимация, които рисуват върху платно, като рендеринга на платно на Lottie, Rive и ExodeUI, се справят с това мащабиране вместо вас, но се прилага същият компромис.
Фрагмент за внедряване на продукцията
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
}Ключове Principle, които трябва да запомните
- devicePixelRatio = физически пиксели на CSS пиксел (2 на типичните Retina екрани)
- Задайте canvas.width/height на CSS размер × съотношение; запазете размера CSS
- Мащабирайте контекста, така че кодът за рисуване да използва CSS пиксела
- Преизчислете при преоразмеряване, промяна на монитора и мащабиране
- Ограничете съотношението на големи платна за ефективност
Често задавани въпроси
Източници
- Window: devicePixelRatio property — MDN Уеб документи