Веб-приложения, использующие библиотеку Chart.js, нередко требуют
вывода графиков на печать: отчётов, аналитических сводок, финансовых
документов, статистики продаж и технических диаграмм. Стандартный
механизм браузера для печати основан на методе
window.print(), который открывает системное окно подготовки
документа к выводу на принтер.
Простейший пример:
<button id="printBtn">Печать</button>
<script>
document.getElementById('printBtn').addEventListener('click', () => {
window.print();
});
</script>
Метод не принимает параметров и работает синхронно с текущим содержимым DOM-дерева. Всё, что отображается на странице в момент вызова, участвует в формировании печатной версии документа.
Графики в Chart.js рендерятся внутри элемента
<canvas>. При печати возникают несколько важных
особенностей:
Например:
<canvas id="salesChart"></canvas>
const ctx = document.getElementById('salesChart');
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Январь', 'Февраль', 'Март'],
datasets: [{
label: 'Продажи',
data: [120, 190, 300],
backgroundColor: '#4e73df'
}]
}
});
При вызове window.print() браузер выводит текущее
состояние canvas как обычное изображение.
Для качественной печати необходимо формировать отдельное оформление
документа через CSS-медиавыражение @media print.
Базовая структура:
@media print {
body {
background: white;
color: black;
}
}
Все стили внутри блока применяются только при печати.
Обычно интерфейс приложения содержит:
Эти элементы не нужны на бумаге.
<div class="toolbar">
<button oncl ick="window.print()">
Печать
</button>
</div>
@media print {
.toolbar {
display: none;
}
}
Можно скрывать сразу несколько компонентов:
@media print {
.sidebar,
.navbar,
.filters,
.controls {
display: none;
}
}
Адаптивные графики могут некорректно масштабироваться на бумаге. Наиболее распространённая проблема — слишком маленький canvas.
Для печати удобно задавать фиксированные размеры.
@media print {
canvas {
width: 800px !important;
height: 400px !important;
}
}
Свойство !important часто необходимо, поскольку Chart.js
динамически управляет размерами canvas.
beforeprint и afterprintБраузеры поддерживают события:
beforeprintafterprintОни позволяют изменять графики непосредственно перед печатью.
Пример:
window.addEventListener('beforeprint', () => {
chart.resize(800, 400);
});
window.addEventListener('afterprint', () => {
chart.resize();
});
Здесь:
Анимации иногда приводят к тому, что браузер печатает промежуточный кадр.
Надёжнее полностью отключать их:
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
animation: false
}
});
Либо временно отключать:
window.addEventListener('beforeprint', () => {
chart.options.animation = false;
chart.update();
});
Canvas зависит от разрешения устройства. Для чёткой печати полезно увеличивать внутреннее разрешение.
Пример:
const chart = new Chart(ctx, {
type: 'bar',
data,
options: {
devicePixelRatio: 2
}
});
Чем выше devicePixelRatio, тем более детализированным
становится изображение.
Часто отчёт содержит несколько диаграмм.
<div class="report">
<canvas id="chart1"></canvas>
<canvas id="chart2"></canvas>
<canvas id="chart3"></canvas>
</div>
Для печати важно контролировать переносы страниц.
@media print {
canvas {
page-break-inside: avoid;
break-inside: avoid;
}
}
Большие графики могут разрываться между страницами. Для предотвращения используются специальные CSS-свойства.
@media print {
.chart-block {
page-break-inside: avoid;
margin-bottom: 40px;
}
}
Принудительный перенос:
.page-break {
page-break-before: always;
}
Пример:
<div class="chart-block">
<canvas id="financeChart"></canvas>
</div>
<div class="page-break"></div>
<div class="chart-block">
<canvas id="usersChart"></canvas>
</div>
Экранный интерфейс редко подходит для бумаги. Для печати обычно создают отдельную цветовую схему.
@media print {
body {
background: white;
}
.card {
box-shadow: none;
border: 1px solid #ccc;
}
}
Тени и градиенты желательно убирать:
@media print {
* {
box-shadow: none !important;
text-shadow: none !important;
}
}
Яркие экранные цвета плохо выглядят на чёрно-белом принтере.
Перед печатью можно заменить палитру:
window.addEventListener('beforeprint', () => {
chart.data.datasets[0].backgroundColor = [
'#000000',
'#555555',
'#999999'
];
chart.update();
});
После печати цвета восстанавливаются:
window.addEventListener('afterprint', () => {
chart.data.datasets[0].backgroundColor = [
'#ff6384',
'#36a2eb',
'#4bc0c0'
];
chart.update();
});
Иногда требуется вывести только график без остального интерфейса.
Создаётся новое окно:
function printChart() {
const canvas = document.getElementById('salesChart');
const dataUrl = canvas.toDataURL();
const printWindow = window.open('', '_blank');
printWindow.document.write(`
<html>
<head>
<title>Печать графика</title>
<style>
body {
margin: 0;
text-align: center;
}
img {
width: 100%;
}
</style>
</head>
<body>
<img src="${dataUrl}">
</body>
</html>
`);
printWindow.document.close();
printWindow.focus();
printWindow.print();
}
toBase64ImageChart.js предоставляет удобный метод преобразования графика в изображение.
const image = chart.toBase64Image();
Далее изображение можно вставить в HTML:
const img = document.createElement('img');
img.src = chart.toBase64Image();
document.body.appendChild(img);
Этот подход особенно удобен для печатных отчётов.
Некоторые браузеры нестабильно печатают canvas. Более надёжный вариант — заменять canvas на изображение.
window.addEventListener('beforeprint', () => {
const img = document.createElement('img');
img.src = chart.toBase64Image();
img.id = 'print-image';
chart.canvas.style.display = 'none';
chart.canvas.parentNode.appendChild(img);
});
После печати:
window.addEventListener('afterprint', () => {
const img = document.getElementById('print-image');
if (img) {
img.remove();
}
chart.canvas.style.display = 'block';
});
CSS позволяет задавать параметры страницы.
@page {
size: landscape;
}
Альбомная ориентация особенно полезна для:
Можно также задавать поля:
@page {
margin: 1cm;
}
Некоторые браузеры автоматически уменьшают содержимое.
Для контроля масштаба:
@media print {
html,
body {
width: 100%;
}
.report {
zoom: 90%;
}
}
Иногда необходимо вывести только контейнер отчёта.
<div id="reportArea">
<canvas id="chart"></canvas>
</div>
@media print {
body * {
visibility: hidden;
}
#reportArea,
#reportArea * {
visibility: visible;
}
#reportArea {
position: absolute;
left: 0;
top: 0;
width: 100%;
}
}
Современные layout-механизмы работают в печати не всегда одинаково.
Например, grid иногда нарушает переносы страниц.
Безопасный вариант:
@media print {
.dashboard {
display: block;
}
.chart-block {
margin-bottom: 30px;
}
}
Если приложение использует dark theme, необходимо переопределять стили.
@media print {
body {
background: white !important;
color: black !important;
}
.card {
background: white !important;
}
}
Иногда перед печатью требуется:
Пример:
async function preparePrint() {
await fetchData();
chart.resize(1000, 500);
chart.update();
window.print();
}
Большие панели аналитики требуют отдельной логики.
Типичный подход:
Пример:
@media print {
.dashboard {
display: block;
}
.widget {
width: 100%;
margin-bottom: 40px;
page-break-inside: avoid;
}
.scroll-container {
overflow: visible;
}
}
Печать в браузерах реализована по-разному.
Наиболее распространённые проблемы:
| Проблема | Описание |
|---|---|
| Обрезание canvas | График выходит за пределы страницы |
| Низкое качество | Недостаточное разрешение |
| Потеря цветов | Браузер не печатает фон |
| Масштабирование | Автоматическое уменьшение |
| Переносы | Разрыв графика между страницами |
Особенно внимательно следует тестировать:
Пример типичной структуры:
<body>
<div class="toolbar">
<button oncl ick="window.print()">
Печать
</button>
</div>
<div class="report">
<section class="chart-block">
<h2>Продажи</h2>
<canvas id="salesChart"></canvas>
</section>
<section class="chart-block">
<h2>Пользователи</h2>
<canvas id="usersChart"></canvas>
</section>
</div>
</body>
CSS:
@media print {
.toolbar {
display: none;
}
body {
background: white;
}
.chart-block {
page-break-inside: avoid;
margin-bottom: 40px;
}
canvas {
width: 100% !important;
height: auto !important;
}
}
Jav * aScript:
window.addEventListener('beforeprint', () => {
salesChart.resize(1000, 500);
usersChart.resize(1000, 500);
});
window.addEventListener('afterprint', () => {
salesChart.resize();
usersChart.resize();
});