javascript - ¿Cómo crear barras redondeadas para Bar Chart.js v2?
jquery (3)
Intentando redondear las barras en el gráfico de barras como se encuentra en esta publicación que funciona como se muestra en el jsFiddle proporcionado. Esto es para la versión 1.
En el gráfico que estoy usando, no se carga para que la referencia se
extend
en
Chart.types.Bar.extend
bloquea el script.
Si uso la opción predeterminada, el gráfico no carga problemas.
Tuve que colocar
Chart.types.Bar.extend
al final para que la opción predeterminada se cargara correctamente.
Ejecute y vea esto en pantalla completa.
Intenté implementar esto con mi versión de Chart.js 2.4.0.
Informes de Chrome:
TypeError no capturado: no se puede leer la propiedad ''extender'' de chart.js indefinido
Este código ni siquiera se ejecutará aquí. ¿Por qué está ocurriendo esto? ¿Podría alguien ayudarme?
Este código funciona con una versión anterior de Chart.js 1.0. ¿Alguien podría mostrar cómo podría funcionar esto con la versión Chart.js 2.0? Gracias.
$(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>
Checkout chartjs-top-round-bar es útil
Solo necesitas
import ''chartjs-top-round-bar'';
...
new Chart(''myChart'',
{
options: {
barRoundness: 0.3
}
}
El código que intentaba usar es en realidad para chart.js v1 y, como descubrió, no funciona para chart.js v2 (que es casi una reescritura completa de chart.js).
Para lograr los mismos resultados en chart.js v2, debe extender
Chart.elements.Rectangle
y sobrescribir su método de
draw
para pintar la parte superior redondeada.
Ya existe un método auxiliar chart.js que dibujará un rectángulo redondeado (
Chart.helpers.drawRoundedRectangle
), por lo que lo modificaremos ligeramente y crearemos un nuevo método auxiliar que solo dibujará una parte superior redondeada (en lugar de todos los lados).
// 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;
},
});
A continuación, también tendrá que extender el controlador del gráfico de barras (
Chart.controllers.bar
) y sobrescribir
dataElementType
para usar el nuevo "rectángulo redondeado" para el gráfico en lugar de un rectángulo regular.
Chart.defaults.roundedBar = Chart.helpers.clone(Chart.defaults.bar);
Chart.controllers.roundedBar = Chart.controllers.bar.extend({
dataElementType: Chart.elements.RoundedTopRectangle
});
Por último, modificaremos la configuración del gráfico para usar el nuevo tipo de gráfico creado anteriormente y agregaremos una nueva propiedad de opciones llamada
barRoundness
para controlar qué tan redonda es la parte superior (0 es plano, 1 es un semicírculo).
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)"
},
}
});
Puede ver un ejemplo de trabajo completo en este codepen .
Además, en caso de que desee una apariencia de "parte superior redondeada" ligeramente diferente, aquí hay otro codepen que utiliza un enfoque diferente para dibujar la parte superior (una sola curva cuadrática).
Lo siguiente solo personaliza
Chart.elements.Rectangle.prototype.draw
; no es necesario crear un tipo de gráfico completamente nuevo.
Otros pros:
- Funciona bien con gráficos de barras verticales y horizontales.
- Funciona bien con gráficos de barras apiladas verticales y horizontales: solo redondea el último cuadro de la serie
Problema notorio: debido a que esto solo redondea el cuadro en el último conjunto de datos, si el valor del punto de datos en el último conjunto de datos es <cualquiera de los puntos de datos anteriores, el cuadro superior visual no se redondeará. Sin embargo, si el último punto de datos es negativo y el valor más bajo, redondeará ese cuadro en las esquinas inferiores.
Crédito: el código original pertenece a https://github.com/uffo . El siguiente código y el violín vinculado demuestran valores positivos crecientes en cada conjunto de datos para cada pila, y también modifican algunas de las opciones de radio predeterminadas.
/**Customize the Rectangle.prototype draw method**/
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;
var fullCornerRadius = this._chart.config.options.fullCornerRadius;
var stackedRounded = this._chart.config.options.stackedRounded;
var typeOfChart = this._chart.config.type;
if (cornerRadius < 0) {
cornerRadius = 0;
}
if (typeof cornerRadius == ''undefined'') {
cornerRadius = 0;
}
if (typeof fullCornerRadius == ''undefined'') {
fullCornerRadius = false;
}
if (typeof stackedRounded == ''undefined'') {
stackedRounded = false;
}
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]);
var nextCornerId, nextCorner, width, height, x, y;
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;
// 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);
}
var x_tl, x_tr, y_tl, y_tr, x_bl, x_br, y_bl, y_br;
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);
// bottom right
ctx.quadraticCurveTo(x_br, y_br, x_br, y_br - radius);
ctx.lineTo(x_tr, y_tr + radius);
// top right
fullCornerRadius ? ctx.quadraticCurveTo(x_tr, y_tr, x_tr - radius, y_tr) : ctx.lineTo(x_tr, y_tr, x_tr - radius, y_tr);
ctx.lineTo(x_tl + radius, y_tl);
// top left
fullCornerRadius ? ctx.quadraticCurveTo(x_tl, y_tl, x_tl, y_tl + radius) : ctx.lineTo(x_tl, y_tl, x_tl, y_tl + radius);
ctx.lineTo(x_bl, y_bl - radius);
// bottom left
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);
// Bottom right corner
fullCornerRadius ? ctx.quadraticCurveTo(x_br, y_br, x_br, y_br - radius) : ctx.lineTo(x_br, y_br, x_br, y_br - radius);
ctx.lineTo(x_tr, y_tr + radius);
// top right Corner
fullCornerRadius ? ctx.quadraticCurveTo(x_tr, y_tr, x_tr - radius, y_tr) : ctx.lineTo(x_tr, y_tr, x_tr - radius, y_tr);
ctx.lineTo(x_tl + radius, y_tl);
// top left corner
ctx.quadraticCurveTo(x_tl, y_tl, x_tl, y_tl + radius);
ctx.lineTo(x_bl, y_bl - radius);
// bttom left corner
ctx.quadraticCurveTo(x_bl, y_bl, x_bl + radius, y_bl);
} else {
var lastVisible = 0;
for (var findLast = 0, findLastTo = this._chart.data.datasets.length; findLast < findLastTo; findLast++) {
if (!this._chart.getDatasetMeta(findLast).hidden) {
lastVisible = findLast;
}
}
var rounded = this._datasetIndex === lastVisible;
if (rounded) {
//Positive Value
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
// top right
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
// bottom right
if (fullCornerRadius || typeOfChart == ''horizontalBar'')
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
else
ctx.lineTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
// bottom left
if (fullCornerRadius)
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
else
ctx.lineTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
// top left
if (fullCornerRadius || typeOfChart == ''bar'')
ctx.quadraticCurveTo(x, y, x + radius, y);
else
ctx.lineTo(x, y, x + radius, y);
}else {
ctx.moveTo(x, y);
ctx.lineTo(x + width, y);
ctx.lineTo(x + width, y + height);
ctx.lineTo(x, y + height);
ctx.lineTo(x, y);
}
}
}
ctx.fill();
if (borderWidth) {
ctx.stroke();
}
};
/**Chart Data**/
var data = {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: ''data 0'',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: [
''rgba(255, 99, 132, 1)'',
''rgba(54, 162, 235, 1)'',
''rgba(255, 206, 86, 1)'',
''rgba(75, 192, 192, 1)'',
''rgba(153, 102, 255, 1)'',
''rgba(255, 159, 64, 1)''
],
borderWidth: 0
}, {
label: ''data 1'',
data: [20, 24, 10, 15, 12, 13],
backgroundColor: [
''rgba(255, 159, 64, 1)'',
''rgba(255, 99, 132, 1)'',
''rgba(255, 206, 86, 1)'',
''rgba(54, 162, 235, 1)'',
''rgba(153, 102, 255, 1)'',
''rgba(75, 192, 192, 1)''
],
borderWidth: 0
}, {
label: ''data 2'',
data: [20, 30, 30, 20, 14, 20],
backgroundColor: [
''rgba(75, 192, 192, 1)'',
''rgba(255, 159, 64, 1)'',
''rgba(255, 99, 132, 1)'',
''rgba(255, 206, 86, 1)'',
''rgba(54, 162, 235, 1)'',
''rgba(153, 102, 255, 1)''
],
borderWidth: 0
}]
};
/**Chart Options - Radius options are here**/
var options = {
//Border radius; Default: 0; If a negative value is passed, it will overwrite to 0;
cornerRadius: 10,
//Default: false; if true, this would round all corners of final box;
fullCornerRadius: false,
//Default: false; if true, this rounds each box in the stack instead of only final box;
stackedRounded: false,
elements: {
point: {
radius: 25,
hoverRadius: 35,
pointStyle: ''rectRounded'',
}
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
},
stacked: true,
radius: 25
}],
xAxes: [{
ticks: {
beginAtZero: true
},
stacked: true,
}]
}
};
/**Generate Chart**/
var ctxBar = document.getElementById("myChart");
var myBarChart = new Chart(ctxBar, {
type: ''bar'',
data: data,
options: options
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.js"></script>
<canvas id="myChart" height="300" width="800"></canvas>