Skip to content

TOAST UI Chart

Spread your data on TOAST UI Chart. TOAST UI Chart is Beautiful Statistical Data Visualization library.

Auto Size

chart.width 또는 chart.height 옵션을 auto로 설정하면 차트 컨테이너의 크기로 자동으로 설정된다. 만약 차트 컨테이너의 크기를 상댓값으로 설정하면 차트의 크기가 브라우저 창 크기에 따라 자동으로 변경될 것이다. 또한 responsive 옵션과 함께 사용하면 창 크기가 변경될 때마다 updateOptions API를 따로 호출하지 않아도 차트 크기에 맞는 옵션을 자유롭게 설정해줄 수 있다.

<div id="chart" style="width: 90vw; height: 90vh">
const options = {
  chart: { width: 'auto', height:'auto' }
  responsive: {
    rules: [
      ...
    ]
  }
};

const el = document.getElementById('chart');
const chart = toastui.Chart.areaChart({ el, data, options });

Vue.js LineChart Component

<template>
  <div ref="el" :style="style"></div>
</template>

<script lang="ts">
import {Vue, Component, Prop, Ref, Watch, Emit} from 'vue-property-decorator';
import ToastChart, {
  LineChart as ToastLineChart,
  LineChartOptions,
  LineSeriesData,
} from '@toast-ui/chart';
import type {LineSeriesDataType} from '@toast-ui/chart/types/options';
import '@toast-ui/chart/dist/toastui-chart.min.css';

const CLICK_LEGEND_LABEL = 'clickLegendLabel';
const CLICK_LEGEND_CHECKBOX = 'clickLegendCheckbox';
const SELECT_SERIES = 'selectSeries';
const UNSELECT_SERIES = 'unselectSeries';
const HOVER_SERIES = 'hoverSeries';
const UNHOVER_SERIES = 'unhoverSeries';
const ZOOM = 'zoom';
const RESET_ZOOM = 'resetZoom';

@Component
export default class LineChart extends Vue {
  @Prop({type: Object, required: true})
  readonly data!: LineSeriesData;

  @Prop({type: Object, default: () => new Object()})
  readonly options!: LineChartOptions;

  @Prop({type: [Number, String], default: '100%'})
  readonly width?: number | string;

  @Prop({type: [Number, String], default: '40vh'})
  readonly height?: number | string;

  @Prop({type: [Number, String], default: '12%'})
  readonly minWidth?: number | string;

  @Prop({type: [Number, String], default: '12vh'})
  readonly minHeight?: number | string;

  @Prop({type: [Number, String]})
  readonly maxWidth?: number | string;

  @Prop({type: [Number, String]})
  readonly maxHeight?: number | string;

  @Ref()
  readonly el!: HTMLDivElement;

  chart!: ToastLineChart;

  @Watch('data', {deep: true})
  watchData(newData) {
    this.chart.setData(newData);
  }

  @Watch('options', {deep: true})
  watchOptions(newOptions) {
    this.chart.setOptions(newOptions);
  }

  mounted() {
    this.chart = ToastChart.lineChart({
      el: this.el,
      data: this.data,
      options: this.options,
    });

    this.chart.on(CLICK_LEGEND_LABEL, e => this.clickLegendLabel(e));
    this.chart.on(CLICK_LEGEND_CHECKBOX, e => this.clickLegendCheckbox(e));
    this.chart.on(SELECT_SERIES, e => this.selectSeries(e));
    this.chart.on(UNSELECT_SERIES, e => this.unselectSeries(e));
    this.chart.on(HOVER_SERIES, e => this.hoverSeries(e));
    this.chart.on(UNHOVER_SERIES, e => this.unhoverSeries(e));
    this.chart.on(ZOOM, e => this.zoom(e));
    this.chart.on(RESET_ZOOM, e => this.resetZoom(e));
  }

  beforeDestroy() {
    this.chart.destroy();
  }

  get style() {
    let result = '';
    if (this.width) {
      result += `width: ${this.width};`;
    }
    if (this.height) {
      result += `height: ${this.height};`;
    }
    if (this.minWidth) {
      result += `min-width: ${this.minWidth};`;
    }
    if (this.minHeight) {
      result += `min-height: ${this.minHeight};`;
    }
    if (this.maxWidth) {
      result += `max-width: ${this.maxWidth};`;
    }
    if (this.maxHeight) {
      result += `max-height: ${this.maxHeight};`;
    }
    return result;
  }

  @Emit(CLICK_LEGEND_LABEL)
  clickLegendLabel(event) {
    return event;
  }

  @Emit(CLICK_LEGEND_CHECKBOX)
  clickLegendCheckbox(event) {
    return event;
  }

  @Emit(SELECT_SERIES)
  selectSeries(event) {
    return event;
  }

  @Emit(UNSELECT_SERIES)
  unselectSeries(event) {
    return event;
  }

  @Emit(HOVER_SERIES)
  hoverSeries(event) {
    return event;
  }

  @Emit(UNHOVER_SERIES)
  unhoverSeries(event) {
    return event;
  }

  @Emit(ZOOM)
  zoom(event) {
    return event;
  }

  @Emit(RESET_ZOOM)
  resetZoom(event) {
    return event;
  }

  addData(data: Array<LineSeriesDataType>, category?: string) {
    this.chart.addData(data, category);
  }
}
</script>

Live chart usage

<i18n lang="yaml">
en:
  title: 'ANSWER PLUGIN'

ko:
  title: '엔서 플러그인'
</i18n>

<template>
  <v-container>
    <v-row>
      <line-chart
        ref="chart"
        :data="chartProps.data"
        :options="chartProps.options"
      ></line-chart>
    </v-row>
  </v-container>
</template>

<script lang="ts">
import {Vue, Component, Ref} from 'vue-property-decorator';
import LineChart from '@/chart/LineChart.vue';

@Component({
  components: {
    LineChart,
  },
})
export default class SensorView extends Vue {
  @Ref()
  chart!: LineChart;

  chartProps = {
    options: {
      usageStatistics: false,
      chart: {
        width: 'auto',
        height: 'auto',
        title: '24-hr Average Temperature',
      },
      yAxis: {
        title: 'Temperature (Celsius)',
        pointOnColumn: true,
      },
      xAxis: {
        title: 'Years',
      },
      series: {
        shift: true,
        showDot: false,
        // zoomable: true,
        spline: true,
      },
    },
    data: {
      categories: ['2020', '2021', '2022', '2023'],
      series: [
        {
          name: 'Seoul',
          data: [-3.5, -1.1, 4.0, 11.3],
        },
        {
          name: 'Seattle',
          data: [3.8, 5.6, 7.0, 9.1],
        },
        {
          name: 'Sydney',
          data: [22.1, 22.0, 20.9, 18.3],
        },
        {
          name: 'Moskva',
          data: [-10.3, -9.1, -4.1, 4.4],
        },
        {
          name: 'Jungfrau',
          data: [-13.2, -13.7, -13.1, -10.3],
        },
      ],
    },
  };

  category = 2024;

  created() {
    window.setInterval(() => {
      const seoul = Math.round(Math.random() * 100);
      const seattle = Math.round(Math.random() * 100);
      const sydney = Math.round(Math.random() * 100);
      const moskva = Math.round(Math.random() * 100);
      const jungfrau = Math.round(Math.random() * 100);

      const data = [seoul, seattle, sydney, moskva, jungfrau];
      const category = this.category.toString();

      this.chart.addData(data, category);
      this.category++;
    }, 1000);
  }
}
</script>

Troubleshooting

라이브 차트 에서 addData X축이 하나씩 사라지는 현상

최초에 다음과 같은 코드로 차트 다이어 그램을 생성할 것이다:

const chart = ToastChart.lineChart({
  el: this.el,
  data: this.data,
  options: this.options,
});

이 때, 세 번째 줄에 해당되는 data가 있는데, 이 값을 변경하면 안된다. 값을 추가할 경우에만 addData로 추가해야 한다.

See also

Favorite site