1👍
✅
Charts.js allows you to access the canvas and draw custom stuff, using a simple mechanism.
We could start with just the bar, as a horizontal bar chart with one item and most other stuff disabled:
const data = {
labels: [""],
datasets: [{
label: '100%',
data: [100],
backgroundColor: '#4af',
barThickness: 100
}]
};
const options = {
type: 'bar',
data,
options: {
animation: {duration: 0},
indexAxis: 'y',
layout: {
padding:{
left: 10,
right: 10
}
},
scales: {
x: {
display: false
},
y: {
display: false
}
},
plugins: {
legend: {
display: false
},
tooltip:{
enabled: false
}
}
}
};
const chart = new Chart(document.getElementById("myChart"), options);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.2.0/chart.umd.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<body>
<canvas id="myChart" style="height:250px; width: 90vw; border: 1px solid #ddd "></canvas>
</body>
Now we can access the canvas through a plugin a draw all the rest:
const plugin = {
id: 'customDraw', // to identify the plugin in the chart options
afterDraw: (chart, args, options) => {
const {ctx} = chart;
// read plugin options
const lineWidth = options.lineWidth || 1,
lineColor = options.lineColor || '#000',
textColor = options.textColor || '#000',
textFont = options.textFont,
starAt = options.starAt,
starColor = options.starColor || '#f44';
// get pixel coordinates for our bar, that is
// positioned at y = 0, from x = 0 to x = 100
const yCenter = chart.scales.y.getPixelForValue(0),
yTop = yCenter-50,
yBottom = yCenter+50,
x0 = chart.scales.x.getPixelForValue(0),
x50 = chart.scales.x.getPixelForValue(50),
x100 = chart.scales.x.getPixelForValue(100),
xStar = chart.scales.x.getPixelForValue(starAt);
ctx.save();
ctx.strokeStyle = lineColor;
ctx.lineWidth = lineWidth;
ctx.beginPath();
ctx.moveTo(x50, yTop);
ctx.lineTo(x50, yBottom);
ctx.stroke();
ctx.fillStyle = starColor;
drawStar(ctx, xStar, yCenter, 10);
ctx.textBaseline = "top";
ctx.fillStyle = textColor;
if(textFont){
ctx.font = textFont;
}
ctx.textAlign = "start";
ctx.fillText("Lower", x0, yBottom + 2);
ctx.textAlign = "center";
ctx.fillText("Median", x50, yBottom + 2);
ctx.textAlign = "right";
ctx.fillText("Upper", x100, yBottom + 2);
ctx.restore();
}
};
Full code:
function drawStar(ctx, x0, y0, radius){
//https://stackoverflow.com/a/58043598/16466946
const nSpikes = 5;
ctx.beginPath();
for(let i = 0; i < nSpikes*2; i++){
let rotation = Math.PI/2;
let angle = (i/(nSpikes*2))*Math.PI*2+rotation;
let dist = radius*(i%2)+radius;
let x = x0+Math.cos(angle)*dist;
let y = y0+Math.sin(angle)*dist;
if(i === 0) {
ctx.moveTo(x, y);
continue; //skip
}
ctx.lineTo(x, y);
}
ctx.closePath();
ctx.fill();
}
const plugin = {
id: 'customDraw',
afterDraw: (chart, args, options) => {
const {ctx} = chart;
// read plugin options
const lineWidth = options.lineWidth || 1,
lineColor = options.lineColor || '#000',
textColor = options.textColor || '#000',
textFont = options.textFont,
starAt = options.starAt,
starColor = options.starColor || '#f44';
// get pixel coordinates for our bar, that is
// positioned at y = 0, from x = 0 to x = 100
const yCenter = chart.scales.y.getPixelForValue(0),
yTop = yCenter-50,
yBottom = yCenter+50,
x0 = chart.scales.x.getPixelForValue(0),
x50 = chart.scales.x.getPixelForValue(50),
x100 = chart.scales.x.getPixelForValue(100),
xStar = chart.scales.x.getPixelForValue(starAt);
ctx.save();
ctx.strokeStyle = lineColor;
ctx.lineWidth = lineWidth;
ctx.beginPath();
ctx.moveTo(x50, yTop);
ctx.lineTo(x50, yBottom);
ctx.stroke();
ctx.fillStyle = starColor;
drawStar(ctx, xStar, yCenter, 10);
ctx.textBaseline = "top";
ctx.fillStyle = textColor;
if(textFont){
ctx.font = textFont;
}
ctx.textAlign = "start";
ctx.fillText("Lower", x0, yBottom + 2);
ctx.textAlign = "center";
ctx.fillText("Median", x50, yBottom + 2);
ctx.textAlign = "right";
ctx.fillText("Upper", x100, yBottom + 2);
ctx.restore();
}
};
const data = {
labels: [""],
datasets: [{
label: '100%',
data: [100],
backgroundColor: '#4af',
barThickness: 100
}]
};
const options = {
type: 'bar',
data,
options: {
animation: {duration: 0},
indexAxis: 'y',
layout: {
padding:{
left: 10,
right: 10
}
},
scales: {
x: {
display: false
},
y: {
display: false
}
},
plugins: {
legend: {
display: false
},
tooltip:{
enabled: false
},
customDraw:{
lineWidth: 3,
textFont: '20px serif',
starAt: 23
}
}
},
plugins: [plugin],
};
new Chart(document.getElementById("myChart"), options);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.2.0/chart.umd.js" integrity="sha512-B51MzT4ksAo6Y0TcUpmvZnchoPYfIcHadIaFqV5OR5JAh6dneYAeYT1xIlaNHhhFAALd5FLDTWNt/fkxhwE/oQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<body>
<canvas id="myChart" style="height:250px; width: 90vw; border: 1px solid #ddd "></canvas>
</body>
Next step would be to add custom interaction – tooltips, click events and everything…
Source:stackexchange.com