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

Chart.js环形图仅设置上下边框及图例样式问题求助

Chart.js环形图边框与图例样式问题解决

问题

  • 图表环形分段的左右两侧出现三角形边框
  • 图例存在响应式尺寸原生问题,希望仅为图表分段添加CSS样式,不影响图例

当前效果

环形图分段的左右两侧出现多余的三角形边框,图例样式受图表分段的边框设置影响。

预期效果(Figma设计)

仅为环形图的分段设置顶部和底部边框,左右两侧无额外边框;图例样式独立,不受图表分段样式干扰。

当前代码

// 初始化图表配置项
initializeOptions() {
  this.value = this.data[0]?.value || 0;
  this.CIRCLE_OF_DOUGHNUT = '95%';
  
  // 默认边框宽度
  const borderWidths = this.data.map(() => 0); 
  // 第一个元素(最大项)的边框宽度
  borderWidths[0] = 10; 

  this.DATAS = {
    labels: this.data.map(graph => GraphMapping[graph?.category || ""]),
    datasets: [
      {
        label: this.data.map(graph => GraphMapping[graph?.category || ""]),
        data: this.data.map(graph => (graph?.value || 0)),
        backgroundColor: this.LIST_COLORS_VALUES,
        borderColor: this.LIST_COLORS_VALUES[0],
        borderWidth: borderWidths,
        borderAlign: 'outside',
        spacing: 8    
      }
    ]
  };

  // 图表显示配置
  this.GRAPH_OPTIONS = {
    cutout: this.CIRCLE_OF_DOUGHNUT,
    animation: false,
    plugins: {
      title: {
        display: true,
        text: this.titleLabel,
        font: {
          size: 18,
        }
      },
      legend: {
        onClick: (e: any) => {
          // 点击图例时不触发默认行为
          e.native.stopPropagation();
        },
        position: 'bottom',
        labels: {
          color: 'black',
          font: {
            size: 15,
            family: 'Arial',
            weight: 400
          },
          usePointStyle: true,
          pointStyleWidth: 20,
          pointStyleHeight: 20,
        },
        display: true,
      },
      tooltip: {
        enabled: false,
      }
    },
  };
}

// 渲染图表函数
renderChart() {
  const chart = new Chart(this.idSelector, {
    type: 'doughnut',
    data: this.DATAS,
    options: {
      ...this.GRAPH_OPTIONS,
      onHover: (event: any, elements: any[]) => {
        if (elements.length) {
          const element = elements[0];
          this.value = this.data[element.index].value as number; 
          chart.data.datasets[0].label = this.data[element.index].category;
        }
      },
      onClick: (event: any, elements: any[]) => {
        if (elements.length) {
          const element = elements[0];
          const index = element.index;
          this.value = this.data[index].value as number;
          this.lastClickedValue = this.value;
          this.isSegmentClicked = true; 
          // 重置所有边框宽度为0
          (chart.data.datasets[0].borderWidth as number[]).fill(0);
          // 设置点击分段的边框宽度
          (chart.data.datasets[0].borderWidth as number[])[index] = 10;
          chart.data.datasets[0].borderColor = this.LIST_COLORS_VALUES[index];
          chart.update();
        }
      }
    },
    plugins: [{
      id: 'mouseleave',
      afterEvent: (chart, args, element: any[]) => {
        if (args.event.type === 'mouseout') {
          if (!this.isSegmentClicked) this.value = this.data[0]?.value || 0;
          else this.value = this.lastClickedValue;
        }
      }
    }]
  });
}

已尝试方案

分段样式问题

  • 在自定义插件中添加类似mouseleave的逻辑,无效果
  • 尝试设置border: {top: xx, bottom: yy ...},不生效
  • 尝试在点击事件中禁用borderRadius,未解决三角形边框问题

图例问题

  • 尝试设置borderWidth: 0,未解决图例受影响的问题

解决方案

1. 消除左右三角形边框,仅保留上下边框

Chart.js默认会给环形分段绘制完整边框,要实现仅上下边框,需通过自定义插件重绘边框,只保留顶部和底部的弧线:

// 在initializeOptions中初始化自定义边框宽度数组
this.customBorderWidths = this.data.map(() => 0);
this.customBorderWidths[0] = 10;

// 在renderChart的plugins数组中添加自定义边框插件
plugins: [{
  id: 'customBorder',
  afterDraw: (chart) => {
    const ctx = chart.ctx;
    const dataset = chart.data.datasets[0];
    const meta = chart.getDatasetMeta(0);
    const centerX = chart.chartArea.centerX;
    const centerY = chart.chartArea.centerY;
    
    meta.data.forEach((segment, index) => {
      const borderWidth = this.customBorderWidths[index];
      if (borderWidth <= 0) return;
      
      const { outerRadius, innerRadius, startAngle, endAngle } = segment;
      const borderColor = dataset.backgroundColor[index];
      
      ctx.save();
      ctx.strokeStyle = borderColor;
      ctx.lineWidth = borderWidth;
      
      // 绘制顶部外边框弧线
      ctx.beginPath();
      ctx.arc(centerX, centerY, outerRadius, startAngle, endAngle);
      ctx.stroke();
      
      // 绘制底部内边框弧线
      ctx.beginPath();
      ctx.arc(centerX, centerY, innerRadius, startAngle, endAngle);
      ctx.stroke();
      
      ctx.closePath();
      ctx.restore();
    });
    
    // 隐藏默认边框
    meta.data.forEach(segment => {
      segment.borderWidth = 0;
    });
  }
}, {
  id: 'mouseleave',
  // 保留原mouseleave逻辑
  afterEvent: (chart, args, element: any[]) => {
    if (args.event.type === 'mouseout') {
      if (!this.isSegmentClicked) this.value = this.data[0]?.value || 0;
      else this.value = this.lastClickedValue;
    }
  }
}]

2. 隔离图例样式与图表分段样式

通过generateLabels自定义图例项,完全控制图例样式,避免继承图表分段的边框设置:

// 修改GRAPH_OPTIONS中的legend配置
legend: {
  onClick: (e: any) => {
    e.native.stopPropagation();
  },
  position: 'bottom',
  labels: {
    color: 'black',
    font: {
      size: 15,
      family: 'Arial',
      weight: 400
    },
    usePointStyle: true,
    // 自定义图例点样式
    generateLabels: (chart) => {
      return chart.data.labels.map((label, index) => ({
        text: label,
        fillStyle: chart.data.datasets[0].backgroundColor[index],
        strokeStyle: 'transparent',
        lineWidth: 0,
        pointStyle: 'circle',
        pointStyleWidth: 12,
        pointStyleHeight: 12
      }));
    }
  },
  display: true,
}

3. 调整点击事件逻辑

修改点击事件,通过customBorderWidths数组控制边框显示:

onClick: (event: any, elements: any[]) => {
  if (elements.length) {
    const element = elements[0];
    const index = element.index;
    this.value = this.data[index].value as number;
    this.lastClickedValue = this.value;
    this.isSegmentClicked = true; 
    
    // 更新自定义边框宽度数组
    this.customBorderWidths.fill(0);
    this.customBorderWidths[index] = 10;
    
    chart.update();
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 16:24:51