This demo uses imperative chart updates. The generated code is a faithful Angular translation: open it in CodeSandbox to run and tweak.

import { Component, AfterViewInit, OnDestroy, ViewChild } from '@angular/core';
import {
  ChartComponent,
  ApexAxisChartSeries,
  ApexNonAxisChartSeries,
  ApexChart,
  ApexXAxis,
  ApexYAxis,
  ApexTitleSubtitle,
  ApexDataLabels,
  ApexStroke,
  ApexFill,
  ApexLegend,
  ApexTooltip,
  ApexMarkers,
  ApexPlotOptions,
  ApexResponsive,
  ApexGrid,
  ApexAnnotations,
  ApexStates,
  ApexTheme,
  NgApexchartsModule,
} from 'ng-apexcharts';

export type ChartOptions = {
  series?: ApexAxisChartSeries | ApexNonAxisChartSeries;
  chart?: ApexChart;
  xaxis?: ApexXAxis;
  yaxis?: ApexYAxis | ApexYAxis[];
  title?: ApexTitleSubtitle;
  subtitle?: ApexTitleSubtitle;
  dataLabels?: ApexDataLabels;
  stroke?: ApexStroke;
  fill?: ApexFill;
  legend?: ApexLegend;
  tooltip?: ApexTooltip;
  markers?: ApexMarkers;
  plotOptions?: ApexPlotOptions;
  responsive?: ApexResponsive[];
  grid?: ApexGrid;
  annotations?: ApexAnnotations;
  states?: ApexStates;
  theme?: ApexTheme;
  colors?: string[];
  labels?: any;
};

@Component({
  selector: 'app-chart',
  standalone: true,
  imports: [NgApexchartsModule],
  templateUrl: './chart.component.html',
})
export class AppChart implements AfterViewInit, OnDestroy {
  @ViewChild('chart') chart!: ChartComponent;
  private REV: any = [12, 14, 9, 15, 18, 22, 26, 31, 37, 44, 52, 61];

  private MONTHS: any = [
          'Jan',
          'Feb',
          'Mar',
          'Apr',
          'May',
          'Jun',
          'Jul',
          'Aug',
          'Sep',
          'Oct',
          'Nov',
          'Dec',
        ];

  private ACCENT: any = '#0EA5E9';

  private FADE: any = '#e3e8f4';

  private RED: any = '#f87171';

  private QCOLORS: any = ['#0EA5E9', '#7dd3fc', '#4ade80', '#fbbf24'];

  private colors12 = (fn: any): any => {
          var a = []
          for (var i = 0; i < 12; i++) a.push(fn(i))
          return a
        };

  private C_ALL: any = this.colors12(function () {
          return this.ACCENT
        });

  private C_OUTAGE: any = this.colors12(function (i) {
          if (i === 2) return this.RED // March
          return i >= 1 && i <= 3 ? this.ACCENT : this.FADE
        });

  private C_V2: any = this.colors12(function (i) {
          return i >= 4 ? this.ACCENT : this.FADE
        });

  private C_QUARTERS: any = this.colors12(function (i) {
          return this.QCOLORS[Math.floor(i / 3)]
        });

  private COLUMN_OPTS: any = {
          chart: { type: 'bar' },
          series: [{ name: 'Revenue (k$)', data: this.REV }],
          labels: [],
          stroke: { width: 0, colors: undefined },
          plotOptions: {
            bar: { columnWidth: '58%', borderRadius: 3, distributed: true },
          },
          legend: { show: false },
        };

  private columnBeat = (colors: any): any => {
          return Object.assign({}, this.COLUMN_OPTS, { colors: colors })
        };

  private DONUT_OPTS: any = {
          chart: { type: 'donut' },
          series: this.REV,
          labels: this.MONTHS,
          colors: this.C_QUARTERS,
          stroke: { width: 2, colors: ['#fff'] },
          legend: { show: false },
          plotOptions: {
            pie: {
              expandOnClick: false,
              donut: {
                size: '68%',
                labels: {
                  show: true,
                  total: {
                    show: true,
                    label: 'FY25 revenue',
                    formatter: function () {
                      return '341 k$'
                    },
                  },
                },
              },
            },
          },
        };

  private BEATS: any = [
          {
            selector: '#sb-step-1',
            view: { window: { xaxis: null, yaxis: [null] }, theme: { mode: 'light' } },
            this.chartOptions: this.columnBeat(this.C_ALL),
            announce: 'Overview: fiscal 2025 revenue, month by month',
          },
          {
            selector: '#sb-step-2',
            view: {
              window: { xaxis: null, yaxis: [null] },
              theme: { mode: 'light' },
              annotations: {
                static: {
                  xaxis: [
                    {
                      x: 'Mar',
                      strokeDashArray: 4,
                      borderColor: '#f87171',
                      label: {
                        text: 'The outage',
                        borderColor: '#fca5a5',
                        style: { color: '#7f1d1d', background: '#fee2e2' },
                      },
                    },
                  ],
                },
              },
            },
            this.chartOptions: this.columnBeat(this.C_OUTAGE),
            announce: 'Spotlight on March: the outage month',
          },
          {
            selector: '#sb-step-3',
            view: {
              window: { xaxis: null, yaxis: [null] },
              theme: { mode: 'light' },
              annotations: {
                static: {
                  xaxis: [
                    {
                      x: 'May',
                      strokeDashArray: 4,
                      borderColor: '#4ade80',
                      label: {
                        text: 'v2 ships',
                        borderColor: '#86efac',
                        style: { color: '#14532d', background: '#dcfce7' },
                      },
                    },
                  ],
                },
              },
            },
            this.chartOptions: this.columnBeat(this.C_V2),
            announce: 'Spotlight from May on: v2 changes the slope',
          },
          {
            selector: '#sb-step-4',
            view: { window: { xaxis: null, yaxis: [null] }, theme: { mode: 'light' } },
            this.chartOptions: this.columnBeat(this.C_QUARTERS),
            announce: 'The months grouped into quarters by color',
          },
          {
            selector: '#sb-step-5',
            view: { window: { xaxis: null, yaxis: [null] }, theme: { mode: 'light' } },
            this.chartOptions: this.DONUT_OPTS,
            announce: 'The twelve months as a donut ring',
          },
        ];

  private steps: any = document.querySelectorAll('.sb-step');

  private dots: any = document.querySelectorAll('.sb-dot');

  private chip: any = document.getElementById('sb-chip');

  private prevBtn: any = document.getElementById('sb-prev');

  private nextBtn: any = document.getElementById('sb-next');

  private scroller: any = document.getElementById('sb-scroller');

  private current: any = 0;

  private scrollToBeat = (i: any): any => {
          var step = document.getElementById('sb-step-' + (i + 1))
          if (!step || !this.scroller) return
          var sRect = step.getBoundingClientRect()
          var cRect = this.scroller.getBoundingClientRect()
          this.scroller.scrollTop +=
            sRect.top - cRect.top - (this.scroller.clientHeight - step.clientHeight) / 2
        };

  private goToBeat = (i: any): any => {
          i = Math.max(0, Math.min(this.BEATS.length - 1, i))
          this.chart.storyboard.goTo(i)
          this.scrollToBeat(i)
        };

  public chartOptions: Partial<ChartOptions> = {
          series: [
            {
              name: 'Revenue (k$)',
              data: [12, 14, 9, 15, 18, 22, 26, 31, 37, 44, 52, 61],
            },
          ],
          chart: {
            id: 'storyChart',
            type: 'bar',
            height: 300,
            fontFamily: 'Helvetica, Arial, sans-serif',
            animations: { speed: 700, dynamicAnimation: { speed: 500 } },
            toolbar: { show: false },
            zoom: { enabled: false },
          },
          colors: [
            '#0EA5E9',
            '#0EA5E9',
            '#0EA5E9',
            '#0EA5E9',
            '#0EA5E9',
            '#0EA5E9',
            '#0EA5E9',
            '#0EA5E9',
            '#0EA5E9',
            '#0EA5E9',
            '#0EA5E9',
            '#0EA5E9',
          ],
          plotOptions: {
            bar: { columnWidth: '58%', borderRadius: 3, distributed: true },
          },
          stroke: { width: 0 },
          dataLabels: { enabled: false },
          legend: { show: false },
          grid: { borderColor: '#eef0f6' },
          xaxis: {
            categories: [
              'Jan',
              'Feb',
              'Mar',
              'Apr',
              'May',
              'Jun',
              'Jul',
              'Aug',
              'Sep',
              'Oct',
              'Nov',
              'Dec',
            ],
          },
        };
  ngAfterViewInit() {
    (window as any).ApexCharts.setLicense('APEX-eyJleHBpcnlEYXRlIjoiMjEyNi0wNy0wNCIsImlzc3VlRGF0ZSI6IjIwMjYtMDctMjgiLCJwbGFuIjoicHJlbWl1bSIsImRvbWFpbnMiOlsiYXBleGNoYXJ0cy5jb20iLCIxMjcuMC4wLjEiLCJsb2NhbGhvc3QiXSwic2lnIjoieVBmb1VCc0Z3TU9ZdUEyaEZkR0I2Y1FtZ0JITUtXcVdJSjB2NVRESXRZbFR3eDJMUmh6R2x0RUc3VXJ4X0s3b25ZMWRZb2Z2VGItN01ydFYyNDVyOWcifQ==');

    this.chart.addEventListener('beatChange', (c, info) => {
            this.current = info.index
            this.steps.forEach(function (el, i) {
              el.classList.toggle('is-active', i === info.index)
            })
            this.dots.forEach(function (dot, i) {
              dot.classList.toggle('is-active', i === info.index)
            })
            this.chip.textContent = 'Beat ' + (info.index + 1) + ' of ' + this.BEATS.length
            this.prevBtn.disabled = info.index === 0
            this.nextBtn.disabled = info.index === this.BEATS.length - 1
          })
    this.dots.forEach(function (dot, i) {
            dot.addEventListener('click', () => {
              this.goToBeat(i)
            })
          })
    this.prevBtn.addEventListener('click', () => {
            this.goToBeat(this.current - 1)
          })
    this.nextBtn.addEventListener('click', () => {
            this.goToBeat(this.current + 1)
          })
    window.addEventListener('load', () => {
            // scroller binds the observer to the story column, so the story is driven by
            // that panel's own scroll (not the page/iframe viewport).
            this.chart.storyboard.bind({ beats: this.BEATS, scroller: '#sb-scroller' })
          })
  }

  ngOnDestroy() {
    // no cleanup needed
  }
}
Scrollytelling (Storyboard) - Angular Narrative & State | ApexCharts.js | ApexCharts.js