Chart.js: Rounded bar in v2.0

Created on 1 Aug 2016  ·  15Comments  ·  Source: chartjs/Chart.js

Hi there,

First thank you for your amazing library. I need to update the library 1.x to 2.0 for a client and i'm stuck on how to extend correctly my bar chart. I need to apply the same rounded effect on the bar which has been made with v1.0.2 . ChartJS syntax is quite different now.

var originalBarController = Chart.controllers.bar;
Chart.controllers.bar = Chart.controllers.bar.extend({
  draw: function(ease) {
    var i =0;
    originalBarController.prototype.draw.apply(this, arguments);

    /* write here the change to make to corners rounded */

  }

});

Q1 : How can I retrieve the dataset -> bars' array from the extend function ? I only found the bar value with this.chart.chart.config.data.datasets.
Q2 : How can I perform that ?

Any advices appreciated!

JSField : http://jsfiddle.net/0dzp3jxw/

support

Most helpful comment

Here is a working example. CodePen

Unfortunately in the time I had available, I could not get the radius to be set from the chart config so is globally set in the extension.

Tested with Charts.js 2.5.0

Source code: Repo

All 15 comments

@jiboune I think this is going to be a lot easier in V2 (at least the drawing part anyway). You should only have to do:

Chart.elements.Rectangle.prototype.draw = function() {
  // do drawing here including rounded options.
}; 

The bar will have some properties in _view which are used for drawing. If you want to add more you need to modify the bar controller updateElement method

Thank you @etimberg, I'll check this out

Closing as answered

@etimberg do you have a working example of this that you can share? In jsfiddle perhaps.

A working example would help me quite a bit as well.

Here is a working example. CodePen

Unfortunately in the time I had available, I could not get the radius to be set from the chart config so is globally set in the extension.

Tested with Charts.js 2.5.0

Source code: Repo

thanks jedtrow @jedtrow

Any idea on how to accomplish this (rounded bars) in React with react-chartjs-2?

How to apply it only on the top of the bar using the Rounded-Bar-Charts example?

Thanks

Any idea on how to accomplish this (rounded bars) in React with react-chartjs-2?

@mtebele

I found a way to do rounded corners on the top. All you have to do is specify radius 0 for the desired corners. If we're talking about vertical bar charts its the corners 3 & 4, in the case of the horizontal bar charts its the corners 1 & 4.

Here is the relevant code:

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);
if (this._chart.config.type === 'bar') {
    ctx.quadraticCurveTo(x + width, y + height, x + width - 0, y + height);
} else {
    ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
}
ctx.lineTo(x + radius, y + height);
if (this._chart.config.type === 'horizontalBar' || this._chart.config.type === 'bar') {
     ctx.quadraticCurveTo(x, y + height, x, y + height - 0);
} else {
     ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
}
ctx.lineTo(x, y + radius);
if (this._chart.config.type === 'horizontalBar') {
      ctx.quadraticCurveTo(x, y, x + 0, y);
} else {
      ctx.quadraticCurveTo(x, y, x + radius, y);
}

Hope this helps!

@franalt where to add your code snippet?

@PrateekGoyal18 I changed the way I do this for a more effecive one.

Just add this to your HTML, below your chart.js integration:

<script src="js/Chart.roundedBarCharts.js"></script>

And create a Chart.roundedBarCharts.js file in your JS folder whit the following:

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

// If radius is less than 0 or is large enough to cause drawing errors a max
//      radius is imposed. If cornerRadius is not defined set it to 0.
var cornerRadius = this._chart.config.options.cornerRadius;

if(cornerRadius < 0){ cornerRadius = 0; }
if(typeof cornerRadius == 'undefined'){ cornerRadius = 0; }

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;
    }
}

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

// Corner points, from bottom-left to bottom-right clockwise
// | 1 2 |
// | 0 3 |
var corners = [
    [left, bottom],
    [left, top],
    [right, top],
    [right, bottom]
];

// Find first (starting) corner with fallback to 'bottom'
var borders = ['bottom', 'left', 'top', 'right'];
var startCorner = borders.indexOf(borderSkipped, 0);
if (startCorner === -1) {
    startCorner = 0;
}

function cornerAt(index) {
    return corners[(startCorner + index) % 4];
}

// Draw rectangle from 'startCorner'
var corner = cornerAt(0);
ctx.moveTo(corner[0], corner[1]);

for (var i = 1; i < 4; i++) {
    corner = cornerAt(i);
    nextCornerId = i+1;
    if(nextCornerId == 4){
        nextCornerId = 0
    }

    nextCorner = cornerAt(nextCornerId);

    width = corners[2][0] - corners[1][0];
    height = corners[0][1] - corners[1][1];
    x = corners[1][0];
    y = corners[1][1];

    var radius = cornerRadius;

    if (i > 3) {
        radius = 1;
    }

    // Fix radius being too large
    if(radius > Math.abs(height)/2){
        radius = Math.floor(Math.abs(height)/2);
    }
    if(radius > Math.abs(width)/2){
        radius = Math.floor(Math.abs(width)/2);
    }

    if(height < 0){
        // Negative values in a standard bar chart
        x_tl = x;           x_tr = x+width;
        y_tl = y+height;    y_tr = y+height;

        x_bl = x;           x_br = x+width;
        y_bl = y;           y_br = y;


        // Draw
        ctx.moveTo(x_bl+radius, y_bl);
        ctx.lineTo(x_br-radius, y_br);
        ctx.quadraticCurveTo(x_br, y_br, x_br, y_br-radius);
        ctx.lineTo(x_tr, y_tr+radius);
        ctx.quadraticCurveTo(x_tr, y_tr, x_tr-radius, y_tr);
        ctx.lineTo(x_tl+radius, y_tl);
        ctx.quadraticCurveTo(x_tl, y_tl, x_tl, y_tl+radius);
        ctx.lineTo(x_bl, y_bl-radius);
        ctx.quadraticCurveTo(x_bl, y_bl, x_bl+radius, y_bl);

    }else if(width < 0){
        // Negative values in a horizontal bar chart
        x_tl = x+width;     x_tr = x;
        y_tl= y;            y_tr = y;

        x_bl = x+width;     x_br = x;
        y_bl = y+height;    y_br = y+height;

        // Draw
        ctx.moveTo(x_bl+radius, y_bl);
        ctx.lineTo(x_br-radius, y_br);
        ctx.quadraticCurveTo(x_br, y_br, x_br, y_br-radius);
        ctx.lineTo(x_tr, y_tr+radius);
        ctx.quadraticCurveTo(x_tr, y_tr, x_tr-radius, y_tr);
        ctx.lineTo(x_tl+radius, y_tl);
        ctx.quadraticCurveTo(x_tl, y_tl, x_tl, y_tl+radius);
        ctx.lineTo(x_bl, y_bl-radius);
        ctx.quadraticCurveTo(x_bl, y_bl, x_bl+radius, y_bl);

    } else{
        //Positive Value
        ctx.moveTo(x + radius, y);
        ctx.lineTo(x + width - radius, y);
        if (this._chart.config.type === 'sd') {
            ctx.quadraticCurveTo(x + width, y, x + width, y + 0);
        } else {
            ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
        }
        ctx.lineTo(x + width, y + height - radius);
        if (this._chart.config.type === 'bar') {
            ctx.quadraticCurveTo(x + width, y + height, x + width - 0, y + height);
        } else {
            ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
        }
        ctx.lineTo(x + radius, y + height);
        if (this._chart.config.type === 'horizontalBar' || this._chart.config.type === 'bar') {
            ctx.quadraticCurveTo(x, y + height, x, y + height - 0);
        } else {
            ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
        }
        ctx.lineTo(x, y + radius);
        if (this._chart.config.type === 'horizontalBar') {
            ctx.quadraticCurveTo(x, y, x + 0, y);
        } else {
            ctx.quadraticCurveTo(x, y, x + radius, y);
        }
    }
}

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

Hope this helps!

@franalt Many thanks for your snippet, any ideas how adapt him to stacked bars (only style top and bottom of full bar)?

Was this page helpful?
0 / 5 - 0 ratings