Печать через window.print и CSS

Веб-приложения, использующие библиотеку Chart.js, нередко требуют вывода графиков на печать: отчётов, аналитических сводок, финансовых документов, статистики продаж и технических диаграмм. Стандартный механизм браузера для печати основан на методе window.print(), который открывает системное окно подготовки документа к выводу на принтер.

Простейший пример:

<button id="printBtn">Печать</button>

<script>
document.getElementById('printBtn').addEventListener('click', () => {
    window.print();
});
</script>

Метод не принимает параметров и работает синхронно с текущим содержимым DOM-дерева. Всё, что отображается на странице в момент вызова, участвует в формировании печатной версии документа.


Особенности печати canvas-элементов

Графики в Chart.js рендерятся внутри элемента <canvas>. При печати возникают несколько важных особенностей:

  • браузер печатает текущее изображение 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

Браузеры поддерживают события:

  • beforeprint
  • afterprint

Они позволяют изменять графики непосредственно перед печатью.

Пример:

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();
}

Использование toBase64Image

Chart.js предоставляет удобный метод преобразования графика в изображение.

const image = chart.toBase64Image();

Далее изображение можно вставить в HTML:

const img = document.createElement('img');

img.src = chart.toBase64Image();

document.body.appendChild(img);

Этот подход особенно удобен для печатных отчётов.


Печать изображений вместо canvas

Некоторые браузеры нестабильно печатают 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%;
    }
}

Использование flex и grid при печати

Современные 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();
}

Печать dashboard-интерфейсов

Большие панели аналитики требуют отдельной логики.

Типичный подход:

  1. Скрыть интерактивные элементы.
  2. Перестроить сетку.
  3. Увеличить графики.
  4. Удалить прокрутку.
  5. Настроить разрывы страниц.

Пример:

@media print {

    .dashboard {
        display: block;
    }

    .widget {
        width: 100%;
        margin-bottom: 40px;
        page-break-inside: avoid;
    }

    .scroll-container {
        overflow: visible;
    }
}

Ограничения браузеров

Печать в браузерах реализована по-разному.

Наиболее распространённые проблемы:

Проблема Описание
Обрезание canvas График выходит за пределы страницы
Низкое качество Недостаточное разрешение
Потеря цветов Браузер не печатает фон
Масштабирование Автоматическое уменьшение
Переносы Разрыв графика между страницами

Особенно внимательно следует тестировать:

  • Google Chrome;
  • Mozilla Firefox;
  • Safari;
  • Microsoft Edge.

Практическая структура печатного отчёта

Пример типичной структуры:

<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();

});