如何使用 ChartJS 创建圆角矩形

How to create a rounded rectangle using ChartJS

我正在使用 ChartJS 通过添加圆角矩形而不是默认情况下的直边来修改我的条形图。我在 Chart.js 中找到了声明圆角矩形的位置,但我没有正确调用该函数。我想在你声明你的 'barchart' 之后,你可以只添加 x、y、宽度和高度点。你在哪里添加这些?非常感谢任何帮助。

html

<!DOCTYPE html>
<meta charset="utf-8">
<head>
   <title>Bar Chart</title>
   <script src="Scripts/Chart.js"></script>
</head>
<body>
   <style>
      body{
      background-color:transparent;
      }
   </style>
   <div style="width: 25%">
      <canvas id="canvas" height="150" width="225"></canvas>
   </div>
   <script>
      var StandardThroughput = function(){ return Math.round(Math.random()*100)};
      var ActualThroughput = function(){ return Math.round(Math.random()*80)};
      
      var barChartData = {
        labels : ["Standard Throughput","Actual Throughput"],
        datasets : [
            {
                fillColor : "rgba(0,255,0,0.9)",
                strokeColor : "#ffffff",
                //highlightFill: "rgba(220,220,220,0.9)",
                //highlightStroke: "rgba(220,220,220,1)",
                data : [StandardThroughput(),ActualThroughput()]
            }
        
            
        ]
      
      }
      window.onload = function(){
        var ctx = document.getElementById("canvas").getContext("2d");
        
        window.myBar = new Chart(ctx).Bar(barChartData,{
            responsive : true,
            scaleShowGridLines : false,
            scaleShowLabels: false,
            showTooltips: false,
            scaleLineWidth: 0.01,
        });
      }
      
   </script>
</body>

JavaScript

(function(){

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

)};

有了这个,我将扩展基本矩形 class 以覆盖 draw 方法

    Chart.RoundedRectangle = Chart.Rectangle.extend({
        draw: function () {
            var ctx = this.ctx,
                halfWidth = this.width / 2,
                leftX = this.x - halfWidth,
                rightX = this.x + halfWidth,
                top = this.base - (this.base - this.y),
                halfStroke = this.strokeWidth / 2,
                radius = halfWidth;


            // 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 (this.showStroke) {
                leftX += halfStroke;
                rightX -= halfStroke;
                top += halfStroke;
            }

            ctx.beginPath();

            ctx.fillStyle = this.fillColor;
            ctx.strokeStyle = this.strokeColor;
            ctx.lineWidth = this.strokeWidth;


           // stop from creating funky shapes if the radius is bigger than the rectangle we are creating
            if(radius >  (this.base-top)/2)
            {
              radius = (this.base-top)/2;
            }
            ctx.moveTo(leftX, this.base - radius);
            ctx.lineTo(leftX, top + radius);
            ctx.quadraticCurveTo(leftX, top, leftX + radius, top);
            ctx.lineTo(rightX - radius, top);
            ctx.quadraticCurveTo(rightX, top, rightX, top + radius);
            ctx.lineTo(rightX, this.base - radius);
            ctx.quadraticCurveTo(rightX, this.base, rightX - radius, this.base);
            ctx.lineTo(leftX + radius, this.base);
            ctx.quadraticCurveTo(leftX, this.base, leftX, this.base - radius);



            ctx.fill();
            if (this.showStroke) {
                ctx.stroke();
            }
        },

    });

然后声明一个从 Bar 扩展的新图表类型,您可以在其中重写 init 方法以利用这个新的 RoundedRectangle class 注意:您还必须声明 helpers 对象,这样您就不必在新图表中编辑对此的所有引用。

var helpers = Chart.helpers;
Chart.types.Bar.extend({
    name: "MyBar",
    initialize: function (data) {

        //Expose options as a scope variable here so we can access it in the ScaleClass
        var options = this.options;

        this.ScaleClass = Chart.Scale.extend({
            offsetGridLines: true,
            calculateBarX: function (datasetCount, datasetIndex, barIndex) {
                //Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar
                var xWidth = this.calculateBaseWidth(),
                    xAbsolute = this.calculateX(barIndex) - (xWidth / 2),
                    barWidth = this.calculateBarWidth(datasetCount);

                return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth / 2;
            },
            calculateBaseWidth: function () {
                return (this.calculateX(1) - this.calculateX(0)) - (2 * options.barValueSpacing);
            },
            calculateBarWidth: function (datasetCount) {
                //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
                var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);

                return (baseWidth / datasetCount);
            }
        });

        this.datasets = [];

        //Set up tooltip events on the chart
        if (this.options.showTooltips) {
            helpers.bindEvents(this, this.options.tooltipEvents, function (evt) {
                var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];

                this.eachBars(function (bar) {
                    bar.restore(['fillColor', 'strokeColor']);
                });
                helpers.each(activeBars, function (activeBar) {
                    activeBar.fillColor = activeBar.highlightFill;
                    activeBar.strokeColor = activeBar.highlightStroke;
                });
                this.showTooltip(activeBars);
            });
        }

        //Declare the extension of the default point, to cater for the options passed in to the constructor
        this.BarClass = Chart.RoundedRectangle.extend({
            strokeWidth: this.options.barStrokeWidth,
            showStroke: this.options.barShowStroke,
            ctx: this.chart.ctx
        });

        //Iterate through each of the datasets, and build this into a property of the chart
        helpers.each(data.datasets, function (dataset, datasetIndex) {

            var datasetObject = {
                label: dataset.label || null,
                fillColor: dataset.fillColor,
                strokeColor: dataset.strokeColor,
                bars: []
            };

            this.datasets.push(datasetObject);

            helpers.each(dataset.data, function (dataPoint, index) {
                //Add a new point for each piece of data, passing any required data to draw.
                datasetObject.bars.push(new this.BarClass({
                    value: dataPoint,
                    label: data.labels[index],
                    datasetLabel: dataset.label,
                    strokeColor: dataset.strokeColor,
                    fillColor: dataset.fillColor,
                    highlightFill: dataset.highlightFill || dataset.fillColor,
                    highlightStroke: dataset.highlightStroke || dataset.strokeColor
                }));
            }, this);

        }, this);

        this.buildScale(data.labels);

        this.BarClass.prototype.base = this.scale.endPoint;

        this.eachBars(function (bar, index, datasetIndex) {
            helpers.extend(bar, {
                width: this.scale.calculateBarWidth(this.datasets.length),
                x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
                y: this.scale.endPoint
            });
            bar.save();
        }, this);

        this.render();
    },
});

然后您可以像往常一样使用任何图表

示例:http://fiddle.jshell.net/leighking2/fmpu4gyt/

或片段

var helpers = Chart.helpers;
Chart.RoundedRectangle = Chart.Rectangle.extend({
    draw: function () {
        var ctx = this.ctx,
            halfWidth = this.width / 2,
            leftX = this.x - halfWidth,
            rightX = this.x + halfWidth,
            top = this.base - (this.base - this.y),
            halfStroke = this.strokeWidth / 2,
            radius = halfWidth;


        // 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 (this.showStroke) {
            leftX += halfStroke;
            rightX -= halfStroke;
            top += halfStroke;
        }

        ctx.beginPath();

        ctx.fillStyle = this.fillColor;
        ctx.strokeStyle = this.strokeColor;
        ctx.lineWidth = this.strokeWidth;

       
       // stop from creating funky shapes if the radius is bigger than the rectangle we are creating
        if(radius >  (this.base-top)/2)
        {
            radius = (this.base-top)/2;
        }
        ctx.moveTo(leftX, this.base - radius);
        ctx.lineTo(leftX, top + radius);
        ctx.quadraticCurveTo(leftX, top, leftX + radius, top);
        ctx.lineTo(rightX - radius, top);
        ctx.quadraticCurveTo(rightX, top, rightX, top + radius);
        ctx.lineTo(rightX, this.base - radius);
        ctx.quadraticCurveTo(rightX, this.base, rightX - radius, this.base);
        ctx.lineTo(leftX + radius, this.base);
        ctx.quadraticCurveTo(leftX, this.base, leftX, this.base - radius);
        
        
        
        ctx.fill();
        if (this.showStroke) {
            ctx.stroke();
        }
    },

});

Chart.types.Bar.extend({
    name: "MyBar",
    initialize: function (data) {

        //Expose options as a scope variable here so we can access it in the ScaleClass
        var options = this.options;

        this.ScaleClass = Chart.Scale.extend({
            offsetGridLines: true,
            calculateBarX: function (datasetCount, datasetIndex, barIndex) {
                //Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar
                var xWidth = this.calculateBaseWidth(),
                    xAbsolute = this.calculateX(barIndex) - (xWidth / 2),
                    barWidth = this.calculateBarWidth(datasetCount);

                return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth / 2;
            },
            calculateBaseWidth: function () {
                return (this.calculateX(1) - this.calculateX(0)) - (2 * options.barValueSpacing);
            },
            calculateBarWidth: function (datasetCount) {
                //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
                var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);

                return (baseWidth / datasetCount);
            }
        });

        this.datasets = [];

        //Set up tooltip events on the chart
        if (this.options.showTooltips) {
            helpers.bindEvents(this, this.options.tooltipEvents, function (evt) {
                var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];

                this.eachBars(function (bar) {
                    bar.restore(['fillColor', 'strokeColor']);
                });
                helpers.each(activeBars, function (activeBar) {
                    activeBar.fillColor = activeBar.highlightFill;
                    activeBar.strokeColor = activeBar.highlightStroke;
                });
                this.showTooltip(activeBars);
            });
        }

        //Declare the extension of the default point, to cater for the options passed in to the constructor
        this.BarClass = Chart.RoundedRectangle.extend({
            strokeWidth: this.options.barStrokeWidth,
            showStroke: this.options.barShowStroke,
            ctx: this.chart.ctx
        });

        //Iterate through each of the datasets, and build this into a property of the chart
        helpers.each(data.datasets, function (dataset, datasetIndex) {

            var datasetObject = {
                label: dataset.label || null,
                fillColor: dataset.fillColor,
                strokeColor: dataset.strokeColor,
                bars: []
            };

            this.datasets.push(datasetObject);

            helpers.each(dataset.data, function (dataPoint, index) {
                //Add a new point for each piece of data, passing any required data to draw.
                datasetObject.bars.push(new this.BarClass({
                    value: dataPoint,
                    label: data.labels[index],
                    datasetLabel: dataset.label,
                    strokeColor: dataset.strokeColor,
                    fillColor: dataset.fillColor,
                    highlightFill: dataset.highlightFill || dataset.fillColor,
                    highlightStroke: dataset.highlightStroke || dataset.strokeColor
                }));
            }, this);

        }, this);

        this.buildScale(data.labels);

        this.BarClass.prototype.base = this.scale.endPoint;

        this.eachBars(function (bar, index, datasetIndex) {
            helpers.extend(bar, {
                width: this.scale.calculateBarWidth(this.datasets.length),
                x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
                y: this.scale.endPoint
            });
            bar.save();
        }, this);

        this.render();
    },
});



var randomScalingFactor = function () {
    return Math.round(Math.random() * 100)
};

var barChartData = {
    labels: ["January", "February", "March", "April", "May", "June", "July"],
    datasets: [{
        fillColor: "rgba(220,220,220,0.5)",
        strokeColor: "rgba(220,220,220,0.8)",
        highlightFill: "rgba(220,220,220,0.75)",
        highlightStroke: "rgba(220,220,220,1)",
        data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
    }, {
        fillColor: "rgba(151,187,205,0.5)",
        strokeColor: "rgba(151,187,205,0.8)",
        highlightFill: "rgba(151,187,205,0.75)",
        highlightStroke: "rgba(151,187,205,1)",
        data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
    }, {
        fillColor: "rgba(15,18,20,0.5)",
        strokeColor: "rgba(15,18,20,0.8)",
        highlightFill: "rgba(15,18,20,0.75)",
        highlightStroke: "rgba(15,18,20,1)",
        data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
    }]

}
window.onload = function () {
    var ctx = document.getElementById("canvas").getContext("2d");
    window.myBar = new Chart(ctx).MyBar(barChartData);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.1/Chart.js"></script>
<div style="width: 50%">
    <canvas id="canvas" height="450" width="600"></canvas>
</div>

从 V3 开始,这可以通过 chart.js 中的默认选项实现,您可以使用 borderRadius 属性:

const options = {
  type: 'bar',
  data: {
    labels: ["Red", "Blue", "Yellow"],
    datasets: [{
      label: '# of Votes',
      data: [1, 2.5, 1.5],
      backgroundColor: ["Red", "Blue", "Yellow"],
      borderRadius: Number.MAX_SAFE_INTEGER,
      borderSkipped: false
    }]
  },
  options: {}
}

const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
  <canvas id="chartJSContainer" width="600" height="400"></canvas>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.js"></script>
</body>