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

Chart.js v2 水平条形图圆角实现技术求助

搞定Chart.js v2水平条形图的圆角问题

我明白你现在的困扰——把垂直条形图的圆角逻辑改成水平的,卡在二次贝塞尔曲线这儿了对吧?别担心,我一步一步给你讲清楚怎么改。

首先,先搞懂原代码里的drawRoundedTopRectangle是干嘛的:它给垂直条形的顶部两个角做圆角,底部是直角。但水平条形图我们需要的是右侧(条形末端)的两个角做圆角,所以得完全重写这个辅助函数的路径逻辑。

先搞懂二次贝塞尔曲线

Chart.js里的quadraticCurveTo(cpX, cpY, endX, endY)其实很简单:

  • 第一个参数(cpX, cpY)是控制点,它决定了曲线的弯曲方向和弧度大小
  • 第二个参数(endX, endY)是曲线的终点
  • 曲线会从当前画布的位置(上一个路径点)平滑过渡到终点,控制点就是“拉着”曲线变弯的那个点

修改辅助函数适配水平条形图

我们把原函数重命名为drawRoundedEndRectangle(更贴合水平场景),然后重新设计路径:

Chart.helpers.drawRoundedEndRectangle = function(ctx, x, y, width, height, radius) {
  ctx.beginPath();
  // 从条形左下角开始
  ctx.moveTo(x, y + height);
  // 画到左上角
  ctx.lineTo(x, y);
  // 画到右上角的圆角起点(留出半径长度的位置)
  ctx.lineTo(x + width - radius, y);
  // 绘制右上角圆角:控制点在右上角顶点,终点到右侧中间位置
  ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
  // 绘制右下角圆角:控制点在右下角顶点,终点到右侧中间位置
  ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
  // 回到左下角,闭合路径
  ctx.lineTo(x, y + height);
  ctx.closePath();
};

调整条形元素的绘制逻辑

接下来还要修改条形元素的draw方法,主要是两个地方:

  1. 针对水平条形,计算圆角半径时要用条形的高度(而不是宽度),因为圆角是在垂直方向弯曲的
  2. 把调用的辅助函数换成我们新写的drawRoundedEndRectangle

修改后的完整代码如下:

Chart.helpers.drawRoundedEndRectangle = function(ctx, x, y, width, height, radius) {
  ctx.beginPath();
  ctx.moveTo(x, y + height);
  ctx.lineTo(x, y);
  ctx.lineTo(x + width - radius, y);
  ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
  ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
  ctx.lineTo(x, y + height);
  ctx.closePath();
};

Chart.elements.RoundedEndRectangle = Chart.elements.Rectangle.extend({
  draw: function() {
    const ctx = this._chart.ctx;
    const vm = this._view;
    let left, right, top, bottom, signX, signY, borderSkipped;
    let borderWidth = vm.borderWidth;

    if (!vm.horizontal) {
      // 保留原垂直条形的逻辑(如果需要的话)
      left = vm.x - vm.width / 2;
      right = vm.x + vm.width / 2;
      top = vm.y;
      bottom = vm.base;
      signX = 1;
      signY = bottom > top ? 1 : -1;
      borderSkipped = vm.borderSkipped || 'bottom';
    } else {
      // 水平条形的坐标逻辑
      left = vm.base;
      right = vm.x;
      top = vm.y - vm.height / 2;
      bottom = vm.y + vm.height / 2;
      signX = right > left ? 1 : -1;
      signY = 1;
      borderSkipped = vm.borderSkipped || 'left';
    }

    // 边框宽度调整逻辑(保留原逻辑即可)
    if (borderWidth) {
      const barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
      borderWidth = borderWidth > barSize ? barSize : borderWidth;
      const halfStroke = borderWidth / 2;

      const borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0);
      const borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0);
      const borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0);
      const borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0);

      if (borderLeft !== borderRight) {
        top = borderTop;
        bottom = borderBottom;
      }
      if (borderTop !== borderBottom) {
        left = borderLeft;
        right = borderRight;
      }
    }

    // 计算水平条形的圆角半径:用高度的一半乘以roundness
    const barHeight = Math.abs(top - bottom);
    const roundness = 1.0; // 你可以调整这个值控制圆角弧度(0是直角,1是最大圆角)
    const radius = barHeight * roundness * 0.5;

    // 调整条形位置,留出圆角空间
    const prevRight = right;
    right = prevRight - radius;
    const barRadius = prevRight - right;

    ctx.beginPath();
    ctx.fillStyle = vm.backgroundColor;
    ctx.strokeStyle = vm.borderColor;
    ctx.lineWidth = borderWidth;

    // 调用新的圆角绘制函数
    Chart.helpers.drawRoundedEndRectangle(ctx, left, top, prevRight - left, barHeight, barRadius);
    
    ctx.fill();
    if (borderWidth) {
      ctx.stroke();
    }

    // 恢复原始坐标,保证 tooltip 和刻度正常工作
    right = prevRight;
  },
});

Chart.defaults.roundedBar = Chart.helpers.clone(Chart.defaults.bar);
Chart.controllers.roundedBar = Chart.controllers.bar.extend({
  dataElementType: Chart.elements.RoundedEndRectangle
});

关键修改点总结

  • 辅助函数路径完全适配水平条形的右侧圆角,用二次贝塞尔曲线画出两个圆角
  • 水平条形的圆角半径计算改为基于条形高度,这样圆角大小会和条形高度匹配
  • 调整了条形的右侧位置,留出绘制圆角的空间,避免圆角被截断

这样修改后,你的水平条形图就能完美显示右侧圆角啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:08:38