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