Создание анимированного графика

Часто возникает потребность в красивой визуализации данных, включая добавление анимации. К примеру, есть задача показать на совете директоров как менялись продажи товара в разных странах в разрезе лет. Для более красивой и наглядной презентации можно сделать график динамичным, чтобы показать изменения в реальном времени. Для этого воспользуемся виджетом с типом “HTML”.

Создание виджета

В первую очередь создадим виджет и установим соответствующий тип:
Screenshot from 2023-06-28 17-35-30
В HTML виджете доступны 3 вкладки: HTML, CSS, JS. Начнем с подключения библиотеки и добавления верстки для графика.
На вкладке HTML прописываем:

<script src=" https://cdn.jsdelivr.net/npm/echarts@5.4.2/dist/echarts.min.js "></script>
<div class="main"></div>"

Подключаем библиотеку eCharts через CDN(Content Delivery Network) и создаем блок с классом main, в который будем рендерить график.
Следующим шагом настраиваем данные, которые будут отображаться на графике.
Перед созданием графика мы предварительно настроили модель, содержащую ретроспективные данные в разрезе лет. Выбираем нужные на графике показатели в качестве столбцов.

Screenshot from 2023-06-28 17-49-39

Данные в виде таблицы:

Следующим шагом создадим график, заполним его данными и запустим анимацию.
Для этого переходим на вкладку JS.
Инициируем отрисовку графика в элементе, который объявили ранее.

let myChart = echarts.init(document.querySelector('.main'));

Далее объявляем функцию render. Она будет вызываться при каждом открытии виджета и изменении данных(к примеру, при выборе значений в системном фильтре). В функции устанавливаем настройки графика в объекте option:

const option = {
    grid: {
      top: 10,
      bottom: 30,
      left: 150,
      right: 80
    },
    xAxis: {
      max: 'dataMax',
      axisLabel: {
        formatter: function (n) {
          return Math.round(n) + '';
        }
      }
    },
    dataset: {
      source: data.filter(function (d) {
        return d["f_4"].value === startYear;
      })
    },
    yAxis: {
      type: 'category',
      inverse: true,
      max: 10,
      axisLabel: {
        show: true,
        fontSize: 14,
        formatter: function (value) {
            return value;
          return value + '{flag|' + getFlag(value) + '}';
        },
        rich: {
          flag: {
            fontSize: 25,
            padding: 5
          }
        }
      },
      animationDuration: 300,
      animationDurationUpdate: 300
    },
    series: [
      {
        realtimeSort: true,
        seriesLayoutBy: 'column',
        type: 'bar',
        itemStyle: {
          color: function (param) {
            
            return countryColors[param?.value?.name?.value] || '#5470c6';
          }
        },
        encode: {
          x: dimension,
          y: 3
        },
        label: {
          show: true,
          precision: 1,
          position: 'right',
          valueAnimation: true,
          fontFamily: 'monospace'
        }
      }
    ],
    // Disable init animation.
    animationDuration: 0,
    animationDurationUpdate: updateFrequency,
    animationEasing: 'linear',
    animationEasingUpdate: 'linear',
    graphic: {
      elements: [
        {
          type: 'text',
          right: 0,
          bottom: 60,
          style: {
            text: startYear,
            font: 'bolder 80px monospace',
            fill: 'rgba(100, 100, 100, 0.25)'
          },
          z: 100
        }
      ]
    }
  };

График будет отрисован после вызова

myChart.setOption(option);

На данный момент получаем статичную гистограмму:

Анимирование

Анимацию реализуем через периодическое изменение настроек графика(объект option). Для этого внутри функции render объявляем функцию для обновления:

function updateYear(year) {
    let source = data.filter(function (d) {
      return d["f_4"].value  === year;
    });
    let src = source.map(item => [Number(item.f_0.value), Number(item.f_1.value), Number(item.f_2.value), item.name.value, Number(item.f_4.value)])
    option.series[0].data = src;
    option.graphic.elements[0].style.text = year;
    myChart.setOption(option);
  }

Функция принимает аргументом текущий год. Общий набор данных фильтруется по выбранному году и устанавливается в качестве источника. Затем через вызов myChart.setOption(option) происходит обновление графика.
Используем JavaScript функцию setTimeout, чтобы запустить периодический ререндер графика.
Для каждого года из списка запланирован вызов обновления графика через величину, пропорциональную частоте обновления(updateFrequency, устанавливается дополнительно).

  for (let i = startIndex; i < years.length - 1; ++i) {
    (function (i) {
      setTimeout(function () {
        updateYear(years[i + 1]);
      }, (i - startIndex) * updateFrequency);
    })(i);

Теперь значения меняются динамически.
gif_ech