【问题标题】:Flickering of charts and getcontext error with chartjs in the context of VuejsVuejs上下文中chartjs的闪烁图表和getcontext错误
【发布时间】:2021-04-08 19:12:21
【问题描述】:

您好,我正在尝试通过调用 API 来使用 chartjs 显示不同的图表。下面的代码显示了我如何格式化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>

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

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

【问题讨论】:

  • 你考虑过使用 vue-chartjs 吗?
  • 是的,最初我尝试使用 vue-chartjs,但图表没有显示在浏览器中。所以我决定我将使用 chartjs 显示图表
  • 我在工作中使用 vue-chartjs 没有任何问题。错误日志说,ctx 为空,首先检查 canvas 元素是否真的存在。
  • 能否请您在代码沙箱中分享您的 vue-chartjs,我将通过它,并尝试在我的项目中实现相同的东西。
  • 我应该在数据中定义画布吗?请告诉我应该在我的代码中进行哪些更改才能完成结果

标签: vue.js chart.js


【解决方案1】:

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

// 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");
}

进行以下更改:

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>

查看sandbox

【讨论】:

  • 能否请您修改我的代码并将其发送给我,因为我现在有点困惑。我想做的是,我试图通过在方法中使用 API 来显示两个不同的图表。因为我的图表具有相同的 API,但仅在端点不同,即在我的情况下是 ID。请修改我的代码并将其发送给我。 @tuhin47 答案实际上对我有用,但我遇到了闪烁的问题。
  • 谢谢@serg,我已经了解了 Vuejs 的基础知识,现在我了解了 Vuejs 中的代码流程,所以当你第一次回答我时,只有我有点困惑。而且我尝试了您已更新的答案,但不知道为什么它对我有用,即使我已经定义了它,在控制台中它也会显示 id undefined 。那么你认为我应该做哪些改变。我唯一的目标是创建一个 Chart.vue 组件,而不是拥有单独的图表组件。请通过查看我的代码或您的答案告诉我我能做什么。如果你能告诉我,它真的对我有帮助
  • @Quantum 好的,我已经更新了我的答案。检查,如果它是你正在寻找的东西
  • 非常感谢您的耐心和更新答案,这看起来非常好,但是您问我为什么我在代码中使用插槽,从您的回答中,两个图表都将显示在App.vue 即一个接一个,我想要两个图表,但是在我的 App.vue 中我使用了许多 div,所以我想要第一个 div 中的图表一个和第五个 div 中的图表两个,这是使用插槽然后的原因在 App.vue 中指定画布。所以你有什么想法,请分享给我。
  • @Quantum 很高兴为您提供帮助。如果您真的想使用插槽,我建议您将两个答案的方法结合起来。答案已经提供了让它工作所需的一切。如果还有问题,请告诉我。
【解决方案2】:

我在您的代码中发现了几个错误。我将它们修复在Sandbox

对于 Chat.vue

  • 我将文件重命名为ChartDisplay.vue,与组件名称类似
  • 导入chart.js包以使用Chart()函数
  • 我使用演示 API
<template>
      <div
        class="chart-container"
        style="position: relative; height: 40vh; width: 100%"
      >
        <slot name="test1"></slot>
        <slot name="test2"></slot>
      </div>
    </template>

<script>
  import Chart from "chart.js";
  export default {
    name: "ChartDisplay",
    data() {
      return {
        date: [],
        challenge: [],
        data: [],
      };
    },
    mounted() {
      this.check(8, "chart_8");
      this.check(7, "chart_7");
    },
    methods: {
      check(id, name) {
        fetch(
            "https://api.wirespec.dev/wirespec/stackoverflow/fetchchartdataforvuejs"
          )
          .then((response) => response.json())
          .then((response) => {
            this.date = response.date;
            this.challenge = response.challenge;
            this.data = this.date.map((date, index) => ({
              x: new Date(date * 1000),
              y: this.challenge[index],
            }));
            const ctx = document.getElementById([name]).getContext("2d");
            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

  • 您的导入不应带有任何连字符。
  • component 应该是 components
  • 渲染一次组件,以避免闪烁
<template>
  <div>
    <div class="In order to display chart1">
      <chart-display>
        <canvas slot="test1" id="chart_7"></canvas>
        <canvas slot="test2" id="chart_8"></canvas>
      </chart-display>
    </div>
  </div>
</template>

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

【讨论】:

  • 这并不能解决“闪烁”问题,两个图表都在 mounted 钩子中重新呈现。这意味着第一个图表呈现自己和第二个图表,第二个图表相同
  • 非常感谢tuhin47,是的,现在唯一的问题是闪烁,每当我运行代码或刷新页面时,图表都会闪烁几次然后停止,所以我该怎么办。请给我一些想法。
  • 我通过一些修改解决了闪烁的问题。请检查
  • @tuhin47 是的,这解决了闪烁问题。但是原始代码通常有一些暗纹。这不是人们想要使用 Vue 的方式。为什么canvas 作为插槽传递?为什么不能直接在组件中定义?为什么它呈现 2 个图表?等等
  • 你好@tuhin47,我试过你的答案,效果很好,但我现在面临的唯一问题是图表闪烁,它闪烁几次然后停止。我也尝试更改生命周期挂钩,但没有成功,然后我尝试添加 beforeDestroy 但没有任何反应。当我试图显示两个图表时,当页面重新加载或第一次运行时,两个图表都会闪烁。请给我一些想法。
猜你喜欢
  • 2013-09-24
  • 1970-01-01
  • 2017-07-03
  • 2017-03-19
  • 2010-11-15
  • 2011-09-07
  • 1970-01-01
  • 2019-09-05
  • 2014-01-15
相关资源
最近更新 更多