Градиентная заливка с CanvasGradient

Основы работы с CanvasGradient в контексте Chart.js

Градиентная заливка в Chart.js строится на использовании возможностей HTML Canvas API, в частности объекта CanvasGradient. Он создаётся через 2D-контекст холста и применяется как значение свойств визуализации данных, таких как backgroundColor, borderColor и pointBackgroundColor.

Ключевая особенность заключается в том, что градиент нельзя задать статически в конфигурации JSON — он формируется в момент рендеринга, когда доступен контекст canvas.

В Chart.js доступ к контексту осуществляется через:

  • chart.ctx — 2D-контекст холста
  • chart.chartArea — область построения графика
  • scriptable options (context), позволяющие вычислять значения динамически

Создание линейного градиента через createLinearGradient

Линейный градиент создаётся методом:

const gradient = ctx.createLinearGradient(x0, y0, x1, y1);

Параметры задают направление:

  • (x0, y0) — начало градиента
  • (x1, y1) — конец градиента

После создания добавляются цветовые остановки:

gradient.addColorStop(0, 'rgba(75, 192, 192, 0.8)');
gradient.addColorStop(1, 'rgba(75, 192, 192, 0.0)');

Применение градиента в dataset Chart.js

Наиболее частое использование — заполнение области под линией графика:

const ctx = document.getElementById('chart').getContext('2d');

const gradient = ctx.createLinearGradient(0, 0, 0, 400);
gradient.addColorStop(0, 'rgba(54, 162, 235, 0.6)');
gradient.addColorStop(1, 'rgba(54, 162, 235, 0)');

const chart = new Chart(ctx, {
  type: 'line',
  data: {
    labels: ['Янв', 'Фев', 'Мар', 'Апр', 'Май'],
    datasets: [{
      label: 'Продажи',
      data: [10, 25, 18, 30, 22],
      backgroundColor: gradient,
      borderColor: 'rgba(54, 162, 235, 1)',
      fill: true,
      tension: 0.4
    }]
  }
});

Градиент применяется как обычный цвет, но вместо строки используется объект CanvasGradient.


Динамический градиент с учётом области chartArea

Жёстко заданные координаты (например, 400px) приводят к проблемам при ресайзе. Более корректный подход — вычислять градиент после определения области графика.

Используется доступ к chart.chartArea:

const chart = new Chart(ctx, {
  type: 'line',
  data: {
    labels: ['A', 'B', 'C', 'D'],
    datasets: [{
      data: [5, 10, 7, 12],
      fill: true,
      backgroundColor: (context) => {
        const chart = context.chart;
        const { ctx, chartArea } = chart;

        if (!chartArea) {
          return null;
        }

        const gradient = ctx.createLinearGradient(
          0,
          chartArea.top,
          0,
          chartArea.bottom
        );

        gradient.addColorStop(0, 'rgba(255, 99, 132, 0.5)');
        gradient.addColorStop(1, 'rgba(255, 99, 132, 0)');

        return gradient;
      }
    }]
  }
});

Такой подход обеспечивает адаптивность: градиент всегда совпадает с реальной высотой графика.


Scriptable options и функция context

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

backgroundColor: (context) => { ... }

Объект context содержит:

  • chart — экземпляр графика
  • dataIndex — индекс точки
  • dataset — данные набора
  • type — тип элемента

Это позволяет строить разные градиенты в зависимости от данных.

Пример градиента, зависящего от значения:

backgroundColor: (context) => {
  const value = context.raw;

  const ctx = context.chart.ctx;
  const chartArea = context.chart.chartArea;

  if (!chartArea) return null;

  const gradient = ctx.createLinearGradient(
    0,
    chartArea.bottom,
    0,
    chartArea.top
  );

  if (value > 20) {
    gradient.addColorStop(0, 'rgba(40, 167, 69, 0.6)');
    gradient.addColorStop(1, 'rgba(40, 167, 69, 0)');
  } else {
    gradient.addColorStop(0, 'rgba(220, 53, 69, 0.6)');
    gradient.addColorStop(1, 'rgba(220, 53, 69, 0)');
  }

  return gradient;
}

RadialGradient: круговая заливка

Кроме линейного градиента используется createRadialGradient:

const gradient = ctx.createRadialGradient(
  200, 200, 0,
  200, 200, 200
);

gradient.addColorStop(0, 'rgba(255, 206, 86, 0.8)');
gradient.addColorStop(1, 'rgba(255, 206, 86, 0)');

Параметры:

  • x0, y0, r0 — центр и начальный радиус
  • x1, y1, r1 — конечный радиус и смещение

В Chart.js такой градиент часто применяется для точечных графиков и bubble chart.


Использование градиента в разных типах графиков

Line chart

Основное применение — заливка под линией:

  • fill: true
  • backgroundColor: CanvasGradient

Bar chart

Каждая колонка может иметь собственный градиент:

backgroundColor: (context) => {
  const chart = context.chart;
  const { ctx, chartArea } = chart;

  if (!chartArea) return null;

  const gradient = ctx.createLinearGradient(0, chartArea.bottom, 0, chartArea.top);
  gradient.addColorStop(0, 'rgba(54, 162, 235, 0.2)');
  gradient.addColorStop(1, 'rgba(54, 162, 235, 0.9)');

  return gradient;
}

Doughnut / Pie chart

Используется радиальный градиент для эффекта объёма:

const gradient = ctx.createRadialGradient(150, 150, 50, 150, 150, 150);
gradient.addColorStop(0, '#4bc0c0');
gradient.addColorStop(1, '#1e7f7f');

Повторное использование и кэширование градиентов

Создание градиента на каждый рендер может быть затратным. Оптимизация заключается в кэшировании:

let cachedGradient = null;

function getGradient(chart) {
  const { ctx, chartArea } = chart;

  if (!chartArea) return null;
  if (cachedGradient) return cachedGradient;

  const gradient = ctx.createLinearGradient(
    0,
    chartArea.bottom,
    0,
    chartArea.top
  );

  gradient.addColorStop(0, 'rgba(153, 102, 255, 0.6)');
  gradient.addColorStop(1, 'rgba(153, 102, 255, 0)');

  cachedGradient = gradient;
  return gradient;
}

Кэширование особенно важно при анимации и частых обновлениях данных.


Перерисовка и жизненный цикл графика

Градиенты должны пересоздаваться при изменении:

  • размера canvas
  • данных
  • масштабов осей

Chart.js вызывает повторный рендер, но не всегда пересоздаёт контекстные объекты автоматически. Поэтому использование context.chart.chartArea внутри функции — обязательная практика для стабильной отрисовки.


Типичные ошибки при работе с CanvasGradient

  • использование фиксированных координат вместо chartArea
  • создание градиента до готовности canvas
  • возврат undefined вместо null в scriptable options
  • отсутствие проверки chartArea
  • повторное создание градиента без необходимости

Комбинирование градиента с прозрачностью и режимами смешивания

Дополнительная выразительность достигается через:

  • rgba с альфа-каналом
  • borderColor с высокой насыщенностью
  • pointBackgroundColor для акцентов

Пример:

borderColor: 'rgba(75, 192, 192, 1)',
backgroundColor: gradient,
pointBackgroundColor: 'rgba(75, 192, 192, 1)'

Также можно использовать borderWidth и tension для усиления визуального эффекта плавности.


Градиенты и анимация данных

При обновлении данных градиент остаётся синхронизированным с геометрией графика, если он вычисляется через context. Это позволяет сохранять визуальную целостность при:

  • динамическом добавлении точек
  • live-обновлениях
  • потоковых данных

Градиент становится частью реактивной модели визуализации, а не статическим стилем.