Vuejs上下文中chartjs的闪烁图表和getcontext错误

2022-01-22 00:00:00 vue.js chart.js

您好,我正在尝试通过调用 API 来使用 chartjs 显示不同的图表.下面的代码显示了我如何格式化chart.vue

Chart.vue:

<模板><div 类=图表容器"风格=位置:相对;高度:40vh;宽度:100%;"><插槽名称=test1"></slot><插槽名称=test2"></slot></div></模板><脚本>导出默认{名称:'图表',数据 () {返回 {日期: [],挑战: [],数据: []}},挂载(){this.check(8, 'chart_8')this.check(7, 'chart_7')},方法: {检查(身份证,姓名){this.$http.get(`/api_chart/${ id }/full`).then((响应) => {this.date = response.data.datethis.challenge = response.data.challengethis.data = this.date.map((日期, 索引) => ({x:新日期(日期 * 1000),y: this.challenge[索引]}))const ctx = document.getElementById([name]).getContext('2d')让 myChart = new Chart(ctx, {类型:'线',数据: {数据集:[{标签:挑战",数据:this.data,边框颜色:'#EA5455',}]},选项: {线张力:0,保持纵横比:假,秤:{y轴:[{刻度标签:{显示:假},滴答声:{beginAtZero:是的,回调(值){返回`${value}%`}}}],x轴:[{类型:'时间',时间: {单位:'月'},刻度标签:{显示:真,}}]}}})})}}}</脚本>

App.vue:

<模板><div class="为了显示chart1"><图表显示><画布槽=test1";id="chart_7";></画布></图表显示></div><div class="为了显示chart1"><图表显示><画布槽=test2";id="chart_8";></画布></图表显示></div></模板><脚本>从 './Chart.vue' 导入图表显示导出默认{组件:{图表显示}}</脚本>

如您所见,我已经共享了我的 Chart.vue 和 App.vue,我可以在浏览器中看到我的图表,但是每当我运行代码或刷新页面时,图表都会闪烁并停止.然后在我的控制台中出现以下错误:

请有人帮我解决这个问题,并请告诉我是否应该对我的代码进行任何更改来解决它.请把修改码发给我.

解决方案

正如我在评论中所写,图表被渲染了两次.这会导致闪烁.

//每次使用<chart-display>,都会渲染2个图表,也就是说图表1会渲染//自身和图表 2,char 2 渲染自身和图表 1,这在 Vue 中通常是一个不好的模式挂载(){this.check(8, "chart_8");this.check(7, "chart_7");}

进行以下更改:

ChartDisplay.vue

<template>

App.vue

<template>

<div class="为了显示chart1"><图表显示/></div></div></模板><脚本>从./ChartDisplay.vue"导入 ChartDisplay;导出默认{组件:{ ChartDisplay },};</脚本>

在 沙盒上查看它p>

Hello i am trying to display different charts using the chartjs by calling the API. Below code shows how i have formatted the chart.vue

Chart.vue:

<template>
  <div class="chart-container" style="position: relative; height: 40vh; width:100%;">
    <slot name="test1"></slot>
    <slot name="test2"></slot>
  </div>
</template>

<script>

export default {
  name: 'charts',
  data () {
    return {
      date: [],
      challenge: [],
      data: []
    }
  },
  mounted () {
    this.check(8, 'chart_8')
    this.check(7, 'chart_7')
  },
  methods: {
    check (id, name) {
      this.$http.get(`/api_chart/${ id  }/full`)
        .then((response) => {
          this.date = response.data.date
          this.challenge = response.data.challenge
          this.data = this.date.map((date, index) => ({
            x: new Date(date * 1000),
            y: this.challenge[index]
          }))
          const ctx =  document.getElementById([name]).getContext('2d')
          let myChart = new Chart(ctx, {
            type: 'line',
            data: {
              datasets: [
                {
                  label: 'Challenge',
                  data: this.data,
                  borderColor: ' #EA5455',
                }
              ]
            },
            options: {
              lineTension: 0,
              maintainAspectRatio: false,
              scales: {
                yAxes: [
                  {
                    scaleLabel: {
                      display: false
                    },
                    ticks: {
                      beginAtZero: true,
                      callback (value) {
                        return `${value}%`
                      }
                    }
                  }
                ],
                xAxes: [
                  {
                    type: 'time',
                    time: {
                      unit: 'month'
                    },
                    scaleLabel: {
                      display: true,
                    }
                  }
                ]
              }
            }
          })
        })
    }
  }

}
</script>

App.vue:

<template>
  <div class="In order to display chart1">
    <chart-display>  <canvas slot="test1" id="chart_7" ></canvas> </chart-display>
  </div>
  <div class="In order to display chart1">
    <chart-display>  <canvas slot="test2" id="chart_8" ></canvas> </chart-display>
  </div>
</template>

<script>
import chart-display from './Chart.vue'
export default {
  component: {chart-display}
}
</script>

As you can see i have shared my Chart.vue and App.vue, i am able to see my chart in the browser, but whenever i run the code or refresh the page, the charts flickers and stops. And then in my console i get below error:

Please someone help me to get rid of this issue, and please tell me if any changes i should do in my code to solve it. Please send me the modification code.

解决方案

As I wrote in my comment, the charts are rendered twice. This causes flickering.

// every time you use <chart-display>, 2 charts are rendered, this means chart 1 renders
// itself and chart 2, char 2 renders itself and chart 1, this is a bad pattern in Vue in general

mounted() {
  this.check(8, "chart_8");
  this.check(7, "chart_7");
}

Make the following changes:

ChartDisplay.vue

<template>
  <div
    class="chart-container"
    style="position: relative; height: 40vh; width: 100%"
  >
    <canvas ref="chart_7"></canvas>
    <canvas ref="chart_8"></canvas>
  </div>
</template>

<script>
import Chart from "chart.js";
export default {
  name: "ChartDisplay",
  data() {
    return {
      date: [],
      challenge: [],
      data: [],
      // save charts in an array
      charts: [],
      // charts options
      options: {
        lineTension: 0,
        maintainAspectRatio: false,
        scales: {
          yAxes: [
            {
              scaleLabel: {
                display: false,
              },
              ticks: {
                beginAtZero: true,
                callback(value) {
                  return `${value}%`;
                },
              },
            },
          ],
          xAxes: [
            {
              type: "time",
              time: {
                unit: "month",
              },
              scaleLabel: {
                display: true,
              },
            },
          ],
        },
      },
    };
  },
  mounted() {
    this.render(7, this.$refs.chart_7);
    this.render(8, this.$refs.chart_8);
  },
  methods: {
    render(id, ctx) {
      this.fetchData(id).then((response) => {
        let data = response.date.map((date, index) => ({
          x: new Date(date * 1000),
          y: response.challenge[index],
        }));
        this.charts.push(
          new Chart(ctx, {
            type: "line",
            data: {
              datasets: [
                {
                  label: "Challenge",
                  data: data,
                  borderColor: " #EA5455",
                },
              ],
            },
            options: this.options,
          })
        );
      });
    },
    fetchData(id) {
      return this.$http.get(`/api_chart/${ id  }/full`);
    },
  },
  beforeDestroy() {
    this.charts.forEach((chart) => chart.destroy());
  },
};
</script>
<style >
[v-cloak] {
  display: none;
}
</style>

App.vue

<template>
  <div>
    <div class="In order to display chart1">
      <chart-display/>
    </div>
  </div>
</template>

<script>
import ChartDisplay from "./ChartDisplay.vue";
export default {
  components: { ChartDisplay },
};
</script>

See it on sandbox

相关文章