You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue中Chart.js图表标签不显示,报ctx未定义错误

Chart.js 图表数值不显示且报错"Cannot read properties of undefined (reading 'ctx')"问题排查

问题描述

开发包含柱状图和折线图的Chart.js图表时,无法在图表上显示数值,控制台抛出错误:Cannot read properties of undefined (reading 'ctx'),实际图表的柱状和折线位置均未显示预期的数值标注。

问题代码

<template>
  <div>
    <canvas id="miGrafico"></canvas>
  </div>
</template>

<script>
// Importa Chart.js
import { Chart } from 'chart.js/auto';

export default {
  data() {
      return {
      };
  },
  mounted() {
    const canvasElement = document.getElementById('miGrafico');
    const ctx2 = canvasElement.getContext('2d');

    // Datos de ejemplo para las jornadas
    const jornadas = ['J1', 'J2', 'J3', 'J4', 'J5'];
    const puntuaciones = [10, 15, 8, 20, 12];
    const goles = [1, 0, 2, 1, 3];
    const asistencias = [3, 2, 4, 1, 2];

    new Chart(ctx2, {
      type: 'bar', // Tipo de gráfico de barras
      data: {
        labels: jornadas,
        datasets: [
          {
            label: 'Puntuación',
            data: puntuaciones,
            backgroundColor: '#0493FE', // Color de las barras de puntuación
            barPercentage: 0.3, // Reduce el ancho de las barras de puntuación
            categoryPercentage: 0.8,
            order: -1, // Coloca las barras de puntuación detrás de las líneas
          },
          {
            label: 'Goles',
            data: goles,
            backgroundColor: 'transparent', // No rellena las barras de goles
            borderColor: '#25D983', // Color de la línea de goles
            borderWidth: 2, // Grosor de la línea de goles
            type: 'line', // Mostrar los goles como una línea
            fill: false, // No llenar el área bajo la línea de goles
            order: -2, // Coloca las líneas de goles delante de las barras de puntuación
            pointRadius:0
          },
          {
            label: 'Asistencias',
            data: asistencias,
            backgroundColor: 'transparent', // No rellena las barras de asistencias
            borderColor: '#FF922D', // Color de la línea de asistencias
            borderWidth: 2, // Grosor de la línea de asistencias
            type: 'line', // Mostrar las asistencias como una línea
            fill: false, // No llenar el área bajo la línea de asistencias
            order: -3, // Coloca las líneas de asistencias delante de las barras de puntuación
            pointRadius: 0
          },
        ],
      },
      options:{
        animation: {
            duration: 1,
            onComplete: function () {
                var chartInstance = this.chart,
                ctx = chartInstance.ctx;

                ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
                ctx.textAlign = 'center';
                ctx.textBaseline = 'bottom';
                ctx.fillStyle = 'white';
                var ultimo = [0, 0, 0, 0];
                this.data.datasets.forEach(function (dataset, i) {
                    if(i==0){
                        var meta = chartInstance.controller.getDatasetMeta(i);
                        meta.data.forEach(function (bar, index) {
                            var data = Math.round(dataset.data[index]-0.1);
                            ultimo[index] = data;                        
                            ctx.fillText(data, bar._model.x, bar._model.y - 5);
                        });
                    }
                    if(i==1){
                        var meta = chartInstance.controller.getDatasetMeta(i);
                        meta.data.forEach(function (bar, index) {
                            var data = dataset.data[index];
                            if(data!=ultimo[index]){
                                ctx.fillText(data, bar._model.x, bar._model.y - 5);
                            }                      
                        });
                    }
                    if(i==2){
                        var meta = chartInstance.controller.getDatasetMeta(i);
                        meta.data.forEach(function (bar, index) {
                            var data = parseInt(dataset.data[index]*2);
                            if(data!=0){
                                ctx.fillText(data, bar._model.x, bar._model.y - 5);
                            }                          
                        });
                    }
                });
            }
          }
      },
    });
  },
};
</script>

问题原因

  1. Chart.js v3+ API变更:你使用的是Chart.js v3版本,onComplete回调内的this.chart已被移除,导致chartInstance为undefined,进而读取ctx时触发报错。
  2. 旧版字体配置API失效:Chart.defaults.global在v3中已改为Chart.defaults.font,原写法无法正确设置字体样式,影响数值渲染。
  3. 折线图数据点半径设置错误:将折线图的pointRadius设为0,导致无法获取到数据点的坐标信息,折线图的数值无法定位显示。

修复方案及代码

调整animation.onComplete的回调逻辑,适配Chart.js v3的API,同时修正折线图的点半径配置:

<template>
  <div>
    <canvas id="miGrafico"></canvas>
  </div>
</template>

<script>
import { Chart } from 'chart.js/auto';

export default {
  mounted() {
    const canvasElement = document.getElementById('miGrafico');
    const ctx2 = canvasElement.getContext('2d');

    const jornadas = ['J1', 'J2', 'J3', 'J4', 'J5'];
    const puntuaciones = [10, 15, 8, 20, 12];
    const goles = [1, 0, 2, 1, 3];
    const asistencias = [3, 2, 4, 1, 2];

    new Chart(ctx2, {
      type: 'bar',
      data: {
        labels: jornadas,
        datasets: [
          {
            label: 'Puntuación',
            data: puntuaciones,
            backgroundColor: '#0493FE',
            barPercentage: 0.3,
            categoryPercentage: 0.8,
            order: -1,
          },
          {
            label: 'Goles',
            data: goles,
            backgroundColor: 'transparent',
            borderColor: '#25D983',
            borderWidth: 2,
            type: 'line',
            fill: false,
            order: -2,
            pointRadius: 3, // 设为可见半径,保证坐标可获取
            pointStyle: 'circle'
          },
          {
            label: 'Asistencias',
            data: asistencias,
            backgroundColor: 'transparent',
            borderColor: '#FF922D',
            borderWidth: 2,
            type: 'line',
            fill: false,
            order: -3,
            pointRadius: 3, // 设为可见半径,保证坐标可获取
            pointStyle: 'circle'
          },
        ],
      },
      options:{
        animation: {
          duration: 1,
          // 接收chart实例作为参数
          onComplete: function(chart) {
            const ctx = chart.ctx;
            // 使用v3的字体配置API
            const font = Chart.defaults.font;
            ctx.font = `${font.size}px ${font.family}`;
            ctx.textAlign = 'center';
            ctx.textBaseline = 'bottom';
            ctx.fillStyle = 'white';
            const ultimo = [0, 0, 0, 0];

            chart.data.datasets.forEach((dataset, i) => {
              const meta = chart.getDatasetMeta(i);
              meta.data.forEach((element, index) => {
                if(i === 0){
                  const data = Math.round(dataset.data[index] - 0.1);
                  ultimo[index] = data;
                  ctx.fillText(data, element.x, element.y - 5);
                }
                if(i === 1){
                  const data = dataset.data[index];
                  if(data !== ultimo[index]){
                    ctx.fillText(data, element.x, element.y - 10);
                  }
                }
                if(i === 2){
                  const data = parseInt(dataset.data[index] * 2);
                  if(data !== 0){
                    ctx.fillText(data, element.x, element.y - 15);
                  }
                }
              });
            });
          }
        }
      },
    });
  },
};
</script>

内容的提问来源于stack exchange,提问作者Corboss

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 09:16:14