Skip to content

Instantly share code, notes, and snippets.

@nwkm
Created June 28, 2019 09:48
Show Gist options
  • Save nwkm/ed32a34ea66d156b3af9dbaa92070a53 to your computer and use it in GitHub Desktop.
Save nwkm/ed32a34ea66d156b3af9dbaa92070a53 to your computer and use it in GitHub Desktop.
Chart helper to get rounded bar
//draws a rectangle with a rounded top
Chart.helpers.drawRoundedTopRectangle = function(
ctx: any,
x: number,
y: number,
width: number,
height: number,
radius: number,
) {
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 as any).elements.RoundedTopRectangle = (Chart as any).elements.Rectangle.extend(
{
draw() {
const ctx = this._chart.ctx;
const vm = this._view;
let left;
let right;
let top;
let bottom;
let signX;
let signY;
let borderSkipped;
let borderWidth = vm.borderWidth;
if (!vm.horizontal) {
// bar
left = vm.x - vm.width / 2;
right = vm.x + vm.width / 2;
top = vm.y;
// If the y-axis stack is true, we should increase bottom value by xxx
// vm.base + xxx ===> Help to lower the stacked bar to make they look nice
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.
const barSize = Math.min(
Math.abs(left - right),
Math.abs(top - bottom),
);
borderWidth = borderWidth > barSize ? barSize : borderWidth;
const halfStroke = borderWidth / 2;
// Adjust borderWidth when bar top position is near vm.base(zero).
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);
// 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
const barWidth = Math.abs(left - right);
const roundness = this._chart.config.options.barRoundness || 0.5;
const radius = barWidth * roundness * 0.5;
// keep track of the original top of the bar
const prevTop = top;
// move the top down so there is room to draw the rounded top
top = prevTop + radius;
const 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;
},
},
);
Chart.defaults.roundedBar = Chart.helpers.clone(Chart.defaults.bar);
Chart.controllers.roundedBar = Chart.controllers.bar.extend({
dataElementType: (Chart as any).elements.RoundedTopRectangle,
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment