Search code examples
vue.jsgraphchartschart.jsvuejs3

JS Vue3 dynamic bar chart graph with real-time reactive data


Goal

I'm using Vue3 with the Composition API. I want to stream real-time data into a chart / graph (60 fps, possibly multiple streams / multiple updating arrays). To simplify for this question, I want to create a bar chart that updates it bars reactively to data changes by a button.

Attempts

I tried various JavaScript chart libraries like PrimeVue (Chart.js under the hood), vue-chartjs,Vue-ECharts, plotly.js. However, I'm still struggling either getting it to work or getting smooth animation. I thought real-time plotting would be a more common case, but I have a hard time finding examples (or my Google-foo fails me).

PrimeVue attempt

My best progress is with PrimeVue. I got the reactive part to work, but the issue is that the whole graph animates from scratch at each data update.

// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import PrimeVue from 'primevue/config';
import Chart from 'primevue/chart';

const app = createApp(App);
app.use(PrimeVue);
app.component('Chart', Chart);
app.mount("#app");
<script setup lang="ts">
import { reactive, ref } from 'vue'

const chartValues = reactive([0.3, 1])

const basicData = ref({
  labels: ['January', 'February'],
  datasets: [
    {
      label: 'My First dataset',
      backgroundColor: '#42A5F5',
      data: chartValues
    },
    {
      label: 'My Second dataset',
      backgroundColor: '#FFA726',
      data: [0.4, 0.1]
    }
  ]
  });

const horizontalOptions = ref(
{
  // animation: {
  //   duration: 0
  // }
}
);

function increment() {
  chartValues[0] += 0.1
  if (chartValues[0] > 1) {
    chartValues[0] = 0
  }

}
</script>


<template>
  <h1>PrimeVue chart 2</h1>
  <button @click="increment">count is: {{ chartValues[0] }}</button>
  <div>
  <div class="card">
    <h5>Horizontal</h5>
    <Chart type="bar" :data="basicData" :options="horizontalOptions" />
  </div>
</div>
</template>

I can prevent the whole graph animation by setting animation: {duration: 0}, but this does not look dynamic/fluid. There is no transition between values.

Questions

It would be helpful if you could:

  • (best) Share a code snippet / link to a demo (any chart/graph library is okay) that only updates bars in a bar chart for which the data has changed (or line point in a line chart) with a smooth transition (or anything related to a single array) using Vue3.
  • Recommend a good JS chart library for smooth real-time plot animation that works with Vue3's reactive() / ref().
  • Any other advice that would help me

Solution

  • With the Vue-ApexCharts library and learning from Daniel's answer, I was able to get a reactive bar chart to work.

    Install Vue-ApexCharts in Vue3:

    npm install --save apexcharts
    npm install --save vue3-apexcharts
    

    main.ts

    import { createApp } from 'vue'
    import App from './App.vue'
    import VueApexCharts from "vue3-apexcharts";
    
    const app = createApp(App);
    app.use(VueApexCharts);
    app.mount("#app");
    

    App.vue

    <template>
      <div id="app">
        <button @click="increment">Increment bar 1</button>
        <button @click="addSeries">Add data series</button>
        <button @click="incrementExtra">Increment appended series data</button>
        <VueApexCharts :options="chartData.options" :series="chartData.series"/>
      </div>
    </template>
    
    
    <script setup lang="ts">
    import { ref, reactive } from 'vue'
    import VueApexCharts from 'vue3-apexcharts'
    
    const seriesData1 = reactive([0.3, 1])
    const seriesDataExtra = ref([0.1, 0.7])
    
    const chartData = reactive({
      series: [
        {
          name: 'My First series',
          data: seriesData1
        },
        {
          name: 'My Second series',
          data: [0.4, 0.1]
        }
      ],
      options: {
        chart: {
          type: 'bar',
          // https://apexcharts.com/docs/options/chart/animations/
          animations: {
            enabled: true,
            easing: 'linear',
            speed: 200,
            animateGradually: {
              enabled: false
            },
            dynamicAnimation: {
              enabled: true,
              speed: 150
            }
          }
        },
        xaxis: {
          categories: ['January', 'February']
        }
      }
    })
    
    function increment() {
      seriesData1[0] = ((seriesData1[0] * 10 + 1) / 10) % 1  // + 0.1
      // chartData.series[0].data[0] = ((chartData.series[0].data[0] * 10 + 1) / 10) % 1
    
      console.log(seriesData1)
    }
    
    function incrementExtra() {
      seriesDataExtra.value = seriesDataExtra.value.map(element => ((element * 10 + 1) / 10) % 1)
      console.log(seriesDataExtra)
    }
    
    function addSeries() {
      console.log("Add extra series")
      chartData.series.push({
        name: 'My Next series',
        data: seriesDataExtra
      })
    }
    </script>
    

    The above code can update a single bar's data, which triggers only an animation for that bar. This is done by:

    1. Creating a reactive variable: const seriesData1 = reactive([0.3, 1])
    2. With data: seriesData1 we assign this reactive array to the Chart
    3. The first button triggers the function increment, which increments the first bar's data by 0.1: seriesData1[0] = ((seriesData1[0] * 10 + 1) / 10) % 1

    Note that for step 3 we could have also directly updated the data in the Chart with chartData.series[0].data[0] = ((chartData.series[0].data[0] * 10 + 1) / 10) % 1 and this would give the same effect (this line of code also updates the variable seriesData1).

    However, by updating the reactive variable seriesData1, we can separate data and the chart logic. You don't have to know how to assign data to the chart, but still get your reactive chart by just modifying the data.


    As a bonus I also added a demonstration on how to:

    • Add an extra data stream / series to the plot with the function addSeries and
    • Reactively animate this new data by incrementing all values in a ref array using the function incrementExtra.
    • Modify the animation behavior so it updates all bars at the same time without a bouncy effect (default)

    (video) Vue 3 ApexCharts reactive bar chart

    Demo video: https://i.imgur.com/e5a0y8Z.mp4