如何为 Bar Chart.js v2 创建圆角条?

2022-01-22 00:00:00 jquery javascript chart.js

尝试圆条上的条在这篇文章中找到的图表 与提供的 jsFiddle 中显示的一样.这是针对第 1 版的.

在我使用的图表中,它无法加载对 Chart.types.Bar.extend 中的 extend 的引用使脚本崩溃.

如果我使用默认选项,图表加载没有问题.我必须将 Chart.types.Bar.extend 放在末尾,才能正确加载默认选项.运行并全屏查看.

我尝试使用我的 Chart.js 2.4.0 版本来实现这一点.

Chrome 报告:

<块引用>

未捕获的类型错误:无法读取未定义的属性扩展"图表.js

这段代码甚至不会在这里运行.为什么会出现这种情况?有人可以帮助我吗?

此代码适用于旧版本的 Chart.js 1.0.谁能进一步展示这如何与 Chart.js 2.0 版本一起使用?谢谢.

$(document).ready(function(){var myBarChart1 = new Chart($('#appBarChart2_NoRound'), {类型:'酒吧',数据:dataBar2,选项:选项栏});var ctx = $("#appBarChart2").getContext("2d");var myBarChart2 = new Chart(ctx).BarAlt(dataBarAlt2, {//0(平坦)到 1(更弯曲)曲率:1});});var dataBarAlt2 = {标签:[一月",二月",三月",四月",五月",六月",七月"],数据集:[{填充颜​​色:#1A9BFC",strokeColor: "#1A9BFC",数据:[65、59、80、81、56、55、40],}]};var dataBar2 = {标签:[一月",二月",三月",四月",五月",六月",七月"],数据集:[{标签:我的第一个数据集",背景颜色:'#1A9BFC',边框颜色:'#1A9BFC',边框宽度:1,数据:[65、59、80、81、56、55、40],}]};var optionsBar ={秤:{x轴:[{堆叠:真实,酒吧厚度:20,网格线:{显示:假,}//barPercentage:0.5,}],y轴:[{堆叠:真实,//barPercentage:0.5,}]},传奇: {显示:假,//位置:'左'}};Chart.types.Bar.extend({名称:BarAlt",初始化:函数(数据){Chart.types.Bar.prototype.initialize.apply(this, arguments);if (this.options.curvature !== undefined && this.options.curvature <= 1) {var rectangleDraw = this.datasets[0].bars[0].draw;变种自我=这个;var radius = this.datasets[0].bars[0].width * this.options.curvature * 0.5;//用我们的覆盖矩形绘制this.datasets.forEach(函数(数据集){dataset.bars.forEach(function (bar) {bar.draw = 函数 () {//将原始条向下绘制一点(以便我们的曲线将其带到其原始位置)var y = bar.y;//最小值是必需的,因此动画不会从轴下方开始bar.y = Math.min(bar.y + radius, self.scale.endPoint - 1);//根据我们可以绘制多少曲线来调整条形半径var barRadius = (bar.y - y);rectangleDraw.apply(bar, arguments);//在顶部画一个圆角矩形Chart.helpers.drawRoundedRectangle(self.chart.ctx, bar.x - bar.width/2, bar.y - barRadius + 1, bar.width, bar.height, barRadius);ctx.fill();//恢复y值bar.y = y;}})})}}});

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.js"></脚本><script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<p>条形图 - 工作</p><画布id="appBarChart2_NoRound" height="100" ></div>

<p>圆形条形图 - 不工作</p><canvas id="appBarChart2" height="100" ></div>

解决方案

您尝试使用的代码实际上适用于 chart.js v1,并且正如您所发现的,它不适用于 chart.js v2(这几乎一个完整的chart.js重写).

要在 chart.js v2 中获得相同的结果,您需要扩展 Chart.elements.Rectangle 并覆盖它的 draw 方法以绘制圆顶.已经有一个chart.js 辅助方法会绘制一个圆角矩形(Chart.helpers.drawRoundedRectangle),所以我们将稍微修改它并创建一个新的辅助方法,它只会绘制一个圆角顶部(而不是所有方面).

//绘制一个圆角矩形Chart.helpers.drawRoundedTopRectangle = function(ctx, x, y, width, height, radius) {ctx.beginPath();ctx.moveTo(x + 半径, y);//右上角ctx.lineTo(x + 宽度 - 半径, y);ctx.quadraticCurveTo(x + width, y, x + width, y + radius);//右下角ctx.lineTo(x + 宽度, y + 高度);//左下角ctx.lineTo(x, y + 高度);//左上方ctx.lineTo(x, y + 半径);ctx.quadraticCurveTo(x, y, x + radius, y);ctx.closePath();};Chart.elements.RoundedTopRectangle = Chart.elements.Rectangle.extend({画:函数(){var ctx = this._chart.ctx;var vm = this._view;var 左、右、上、下、signX、signY、borderSkipped;varborderWidth = vm.borderWidth;如果(!vm.horizo​​ntal){//酒吧左 = vm.x - vm.width/2;右 = vm.x + vm.width/2;顶部 = vm.y;底部 = vm.base;符号X = 1;符号Y = 底部>最佳?1:-1;borderSkipped = vm.borderSkipped ||'底部';} 别的 {//水平条左 = vm.base;对 = vm.x;顶部 = vm.y - vm.height/2;底部 = vm.y + vm.height/2;符号X = 右 >剩下?1:-1;符号Y = 1;borderSkipped = vm.borderSkipped ||'剩下';}//画布不允许我们在宽度内描边,所以我们可以//如果我们要在线上设置笔划,请调整大小以适应如果(边框宽度){//borderWidth 应该小于条宽和条高.var barSize = Math.min(Math.abs(左 - 右), Math.abs(上 - 下));边框宽度 = 边框宽度 >条码?barSize:边框宽度;var halfStroke = 边框宽度/2;//当 bar 顶部位置接近 vm.base(zero) 时调整borderWidth.var borderLeft = left + (borderSkipped !== 'left'?halfStroke * signX: 0);var borderRight = right + (borderSkipped !== 'right'?-halfStroke * signX: 0);var borderTop = top + (borderSkipped !== 'top'?halfStroke * signY: 0);var borderBottom = bottom + (borderSkipped !== 'bottom'?-halfStroke * signY: 0);//不会变成垂直线?if (borderLeft !== borderRight) {顶部 = 边界顶部;底部 = 边界底部;}//不会变成水平线?如果(边界顶部!== 边界底部){左=边界左;右=边界右;}}//计算条形宽度和圆度var barWidth = Math.abs(左 - 右);var roundness = this._chart.config.options.barRoundness ||0.5;var 半径 = barWidth * 圆度 * 0.5;//跟踪栏的原始顶部var prevTop = 顶部;//将顶部向下移动,以便有空间绘制圆角顶部顶部 = 上一页顶部 + 半径;var barRadius = top - prevTop;ctx.beginPath();ctx.fillStyle = vm.backgroundColor;ctx.strokeStyle = vm.borderColor;ctx.lineWidth = 边框宽度;//绘制圆角顶部矩形Chart.helpers.drawRoundedTopRectangle(ctx, left, (top - barRadius + 1), barWidth, bottom - prevTop, barRadius);ctx.fill();如果(边框宽度){ctx.stroke();}//恢复原始的顶部值,所以工具提示和比例仍然有效顶部 = 上一页顶部;},});

接下来,您还必须扩展条形图控制器 (Chart.controllers.bar) 并覆盖 dataElementType 以使用新的圆角矩形"作为图表而不是常规的矩形.

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

最后,我们将修改图表的配置以使用上面创建的新图表类型,并添加一个名为 barRoundness 的新选项属性来控制顶部的圆度(0 是平的,1 是半-圆圈).

var ctx = document.getElementById("canvas").getContext("2d");var myBar = new Chart(ctx, {类型:'roundedBar',数据: {标签:[汽车",自行车",步行"],数据集:[{标签:'学生',背景颜色:chartColors.blue,数据: [随机缩放因子(),随机缩放因子(),随机缩放因子(),]}, {标签:'老师',背景颜色:chartColors.red,数据: [随机缩放因子(),随机缩放因子(),随机缩放因子(),]}, {标签:'访客',背景颜色:chartColors.green,数据: [随机缩放因子(),随机缩放因子(),随机缩放因子(),]}]},选项: {响应式:真实,条圆度:1,标题: {显示:真,文本:Chart.js - 圆顶条形图(drawRoundedTopRectangle 方法)"},}});

您可以在此 codepen 中查看完整的工作示例.

此外,如果您想要稍微不同的圆顶"外观,这里还有另一个 codepen 使用不同的方法来绘制顶部(一条二次曲线).

Trying to round the bars on bar chart as found in this post that works as displayed in the jsFiddle provided. This is for version 1.

In the chart that I am using, it fails to load for the reference to extend in Chart.types.Bar.extend crashes the script.

If I use the default option, the chart loads no problems. I had to place the Chart.types.Bar.extend at the end for the default option to load correctly. Run and View this in Full Screen.

I tried implementing this with my version of Chart.js 2.4.0.

Chrome reports:

Uncaught TypeError: Cannot read property 'extend' of undefined chart.js

This code will not even run here. Why is this occurring? Could someone please assist me.

This code works with an older version of Chart.js 1.0. Could anyone please further display how this could work with version Chart.js 2.0? Thank you.

$(document).ready(function(){

	var myBarChart1 = new Chart($('#appBarChart2_NoRound'), {
		type: 'bar',
		data: dataBar2,
		options: optionsBar
	});
  
  var ctx = $("#appBarChart2").getContext("2d");

  	var myBarChart2 = new Chart(ctx).BarAlt(dataBarAlt2, {
		// 0 (flat) to 1 (more curvy)
		curvature: 1
	});
});

var dataBarAlt2 = {
    labels: ["January", "February", "March", "April", "May", "June", "July"],
    datasets: [
        {
            fillColor: "#1A9BFC",
            strokeColor: "#1A9BFC",
            data: [65, 59, 80, 81, 56, 55, 40],
        }
    ]
};

var dataBar2 = {
    labels: ["January", "February", "March", "April", "May", "June", "July"],
    datasets: [
        {
            label: "My First dataset",
            backgroundColor: '#1A9BFC',
            borderColor:'#1A9BFC',
            borderWidth: 1,
            data: [65, 59, 80, 81, 56, 55, 40],
        }
    ]
};

var optionsBar =
		{
        scales: {
            xAxes: [{
                stacked: true,
				barThickness: 20,
				gridLines:{
					display:false,
				}
//				barPercentage:0.5,
            }],
            yAxes: [{
                stacked: true,
				
//				barPercentage:0.5,
            }]
        },
		legend: {
			display: false,
//			position: 'left'
		 }
    };


Chart.types.Bar.extend({
    name: "BarAlt",
    initialize: function (data) {
        Chart.types.Bar.prototype.initialize.apply(this, arguments);

        if (this.options.curvature !== undefined && this.options.curvature <= 1) {
            var rectangleDraw = this.datasets[0].bars[0].draw;
            var self = this;
            var radius = this.datasets[0].bars[0].width * this.options.curvature * 0.5;

            // override the rectangle draw with ours
            this.datasets.forEach(function (dataset) {
                dataset.bars.forEach(function (bar) {
                    bar.draw = function () {
                        // draw the original bar a little down (so that our curve brings it to its original position)
                        var y = bar.y;
                        // the min is required so animation does not start from below the axes
                        bar.y = Math.min(bar.y + radius, self.scale.endPoint - 1);
                        // adjust the bar radius depending on how much of a curve we can draw
                        var barRadius = (bar.y - y);
                        rectangleDraw.apply(bar, arguments);

                        // draw a rounded rectangle on top
                        Chart.helpers.drawRoundedRectangle(self.chart.ctx, bar.x - bar.width / 2, bar.y - barRadius + 1, bar.width, bar.height, barRadius);
                        ctx.fill();

                        // restore the y value
                        bar.y = y;
                    }
                })
            })
        }
    }
});

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<p>Bar Chart - Working</p>
<canvas id="appBarChart2_NoRound" height="100" >
</div>
<div>
<p>Rounded Bar Chart - Not Working</p>
<canvas id="appBarChart2" height="100" >
</div>

解决方案

The code that you were trying to use is actually for chart.js v1 and, as you discovered, does not work for chart.js v2 (which is almost a full chart.js re-write).

To achieve the same results in chart.js v2, you need to extend Chart.elements.Rectangle and overwrite it's draw method in order to paint the rounded top. There is already a chart.js helper method that will draw a rounded rectangle (Chart.helpers.drawRoundedRectangle), so we will modify it slightly and create a new helper method that will only draw a rounded top (instead of all sides).

// draws a rectangle with a rounded top
Chart.helpers.drawRoundedTopRectangle = function(ctx, x, y, width, height, radius) {
  ctx.beginPath();
  ctx.moveTo(x + radius, y);
  // top right corner
  ctx.lineTo(x + width - radius, y);
  ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
  // bottom right   corner
  ctx.lineTo(x + width, y + height);
  // bottom left corner
  ctx.lineTo(x, y + height);
  // top left   
  ctx.lineTo(x, y + radius);
  ctx.quadraticCurveTo(x, y, x + radius, y);
  ctx.closePath();
};

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

    if (!vm.horizontal) {
      // bar
      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 {
      // horizontal bar
      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';
    }

    // Canvas doesn't allow us to stroke inside the width so we can
    // adjust the sizes to fit if we're setting a stroke on the line
    if (borderWidth) {
      // borderWidth shold be less than bar width and bar height.
      var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
      borderWidth = borderWidth > barSize? barSize: borderWidth;
      var halfStroke = borderWidth / 2;
      // Adjust borderWidth when bar top position is near vm.base(zero).
      var borderLeft = left + (borderSkipped !== 'left'? halfStroke * signX: 0);
      var borderRight = right + (borderSkipped !== 'right'? -halfStroke * signX: 0);
      var borderTop = top + (borderSkipped !== 'top'? halfStroke * signY: 0);
      var borderBottom = bottom + (borderSkipped !== 'bottom'? -halfStroke * signY: 0);
      // not become a vertical line?
      if (borderLeft !== borderRight) {
        top = borderTop;
        bottom = borderBottom;
      }
      // not become a horizontal line?
      if (borderTop !== borderBottom) {
        left = borderLeft;
        right = borderRight;
      }
    }

    // calculate the bar width and roundess
    var barWidth = Math.abs(left - right);
    var roundness = this._chart.config.options.barRoundness || 0.5;
    var radius = barWidth * roundness * 0.5;

    // keep track of the original top of the bar
    var prevTop = top;

    // move the top down so there is room to draw the rounded top
    top = prevTop + radius;
    var barRadius = top - prevTop;

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

    // draw the rounded top rectangle
    Chart.helpers.drawRoundedTopRectangle(ctx, left, (top - barRadius + 1), barWidth, bottom - prevTop, barRadius);

    ctx.fill();
    if (borderWidth) {
      ctx.stroke();
    }

    // restore the original top value so tooltips and scales still work
    top = prevTop;
  },
});

Next, you will also have to extend the bar chart controller (Chart.controllers.bar) and overwrite dataElementType to use the new "rounded rectangle" for the chart instead of a regular rectangle.

Chart.defaults.roundedBar = Chart.helpers.clone(Chart.defaults.bar);

Chart.controllers.roundedBar = Chart.controllers.bar.extend({
  dataElementType: Chart.elements.RoundedTopRectangle
});

Lastly, we will modify the chart's config to use the new chart type created above and add a new options property called barRoundness to control how round the top is (0 is flat, 1 is a semi-circle).

var ctx = document.getElementById("canvas").getContext("2d");
var myBar = new Chart(ctx, {
  type: 'roundedBar',
  data: {
    labels: ["Car", "Bike", "Walking"],
    datasets: [{
      label: 'Students',
      backgroundColor: chartColors.blue,
      data: [
        randomScalingFactor(), 
        randomScalingFactor(), 
        randomScalingFactor(), 
      ]
    }, {
      label: 'Teachers',
      backgroundColor: chartColors.red,
      data: [
        randomScalingFactor(), 
        randomScalingFactor(), 
        randomScalingFactor(), 
      ]
    }, {
      label: 'Visitors',
      backgroundColor: chartColors.green,
      data: [
        randomScalingFactor(), 
        randomScalingFactor(), 
        randomScalingFactor(), 
      ]
    }]
  },
  options: {
    responsive: true,
    barRoundness: 1,
    title: {
      display: true,
      text: "Chart.js - Bar Chart with Rounded Tops (drawRoundedTopRectangle Method)"
    },
  }
});

You can see a full working example at this codepen.

Also, in case you want a slightly different "rounded top" look, here is another codepen that uses a different approach to drawing the top (a single quadratic curve).

相关文章