Retina и другие дисплеи с высокой плотностью пикселей используют
повышенный коэффициент devicePixelRatio, из-за чего один
CSS-пиксель фактически отображается на нескольких физических пикселях
экрана. В результате обычный Canvas без дополнительного масштабирования
выглядит размытым: линии теряют чёткость, текст «плывёт», а графики
теряют визуальную точность.
Ключевая причина размытости заключается в различии между:
Если эти значения не синхронизированы, браузер вынужден интерполировать изображение, что приводит к ухудшению качества отрисовки.
Chart.js изначально ориентирован на работу с high-DPI экранами и
автоматически учитывает window.devicePixelRatio.
При создании графика библиотека выполняет пересчёт внутренних размеров canvas:
devicePixelRatioctx.scale(ratio, ratio)Фактически формируется два слоя размеров:
CSS размер: 600 × 300
Bitmap размер: 1200 × 600 (при devicePixelRatio = 2)
Это позволяет добиться чёткого рендеринга без размытия.
В основе лежит работа с CanvasRenderingContext2D и его внутренним transform state.
Основная логика выглядит концептуально следующим образом:
const ratio = window.devicePixelRatio || 1;
canvas.width = cssWidth * ratio;
canvas.height = cssHeight * ratio;
ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
После установки трансформации все последующие операции рисования выполняются в логических координатах, но отрисовываются с повышенной плотностью пикселей.
Chart.js инкапсулирует эту логику внутри своего рендер-цикла, поэтому ручное масштабирование обычно не требуется.
Chart.js позволяет переопределять поведение автоопределения плотности пикселей через глобальную конфигурацию.
Основной параметр:
devicePixelRatioПример конфигурации:
const config = {
type: 'line',
data: {...},
options: {
devicePixelRatio: 2
}
};
Также возможно использование динамического значения:
options: {
devicePixelRatio: window.devicePixelRatio
}
Или фиксация на стандартном значении для предсказуемого рендеринга:
options: {
devicePixelRatio: 1
}
Фиксация на 1 часто применяется в случаях, когда
приоритетом является производительность, а не максимальная чёткость.
При включённом responsive: true Chart.js пересчитывает
размеры canvas при каждом изменении контейнера.
Ключевой момент: масштабирование Retina и адаптивная верстка работают совместно, а не независимо.
При изменении размера контейнера:
devicePixelRatiooptions: {
responsive: true,
maintainAspectRatio: false
}
maintainAspectRatio: false позволяет контейнеру
управлять геометрией графика без принудительного сохранения пропорций,
что особенно важно в сложных grid-layout интерфейсах.
Возникает при несовпадении CSS-размера canvas и его реального bitmap-размера. Основные причины:
canvas.style.width без синхронизации
canvas.widthИногда график выглядит «слишком крупным» или размытым из-за повторного применения масштабирования:
devicePixelRatiotransform: scale(...)Это приводит к конфликту систем координат.
При частых изменениях размеров контейнера (например, при анимации layout) возможны артефакты:
Это связано с тем, что пересоздание bitmap происходит асинхронно относительно рендер-цикла браузера.
В случаях, когда требуется полный контроль над рендерингом, можно отключить автоматическую адаптацию и управлять масштабом самостоятельно.
Подход включает:
ctx.scaledevicePixelRatioПример:
const ratio = 2;
canvas.width = 800 * ratio;
canvas.height = 400 * ratio;
canvas.style.width = '800px';
canvas.style.height = '400px';
const ctx = canvas.getContext('2d');
ctx.scale(ratio, ratio);
В таком режиме важно избегать одновременного вмешательства Chart.js в управление размерами.
Увеличение devicePixelRatio напрямую влияет на объём
вычислений и памяти:
При devicePixelRatio = 2 количество пикселей
увеличивается в 4 раза относительно базового разрешения, при
3 — уже в 9 раз.
Это особенно критично для:
Chart.js использует Canvas API для текста, поэтому качество шрифтов зависит от:
На Retina-дисплеях текст становится визуально ближе к SVG-качеству, если соблюдены условия корректного масштабирования bitmap.
CSS-свойства:
transform: scale()filterzoomмогут нарушать расчёт размеров canvas, так как Chart.js ориентируется на фактические размеры элемента через layout engine.
Особенно критично сочетание:
transform: scale(0.5);
вместе с автоматическим devicePixelRatio, что приводит к
двойной интерпретации масштаба.
Некоторые устройства и браузеры могут изменять
devicePixelRatio динамически:
Chart.js в таких случаях пересоздаёт rendering context и полностью пересчитывает canvas, чтобы сохранить чёткость изображения без артефактов масштабирования.