I'm trying to implement a scroll bar, which adapts to zoom, on a chart made with chart.js. I've found several posts that allow me to implement the scroll bar. So I made a code pen to try it out.
However, I realized that a problem persists. Whenever I zoom in, the scroll bar doesn't adapt to keep covering the whole chart. In other words, after i zoom in, I can no longer scroll over the entire chart. I managed to achieve the desired result with FusionCharts, but not with Chart.js. The "problem" is that way i made this is intrinsic to FusionCharts and there is no code to take inspiration from to implement it with chart.js.
FusionCharts.ready(function() {
var myChart = new FusionCharts({
type: "zoomline",
renderAt: "chart-container",
width: "100%",
height: "100%",
dataFormat: "json",
dataSource
}).render();
});
Here's an example of what I'm trying to achieve.
So I was wondering if anyone has managed to implement such a scroll bar, and if so, how did you proceed?
I completely forgot about this question...
In the end, I managed to implement the scrollbar with a custom inline plugin.
var canvas = document.getElementById('myCanvas'),
ctx = canvas.getContext('2d'),
isDragging,
rectPosition = null;
function generateData(number){
let data = [];
for (let i = 0; i < number ; i++){
data.push(Math.random()*10);
}
return data;
}
function generateLabels(number){
let labels = [];
for (let i = 0; i < number ; i++){
labels.push(i);
}
return labels;
}
function generateDatasets(nbrDatasets, nbrData) {
var datasets = [];
for(let i = 0 ; i < nbrDatasets ; i++ ){
datasets.push({data: generateData(nbrData), pointRadius: 0, pointHoverRadius: 7, label: `Dataset ${i}`});
}
return datasets;
}
var cfg = {
type: 'line',
data:{
labels: generateLabels(100),
datasets: generateDatasets(2, 100),
},
options:{
animations: false,
responsive: true,
maintainAspectRatio: false,
normalized: true,
layout: {
padding: {
bottom: 40,
},
},
interaction:{
mode: 'nearest',
intersect: false,
axis: 'x'
},
plugins:{
zoom:{
zoom:{
drag:{
modifierKey: 'shift',
enabled:true,
},
mode: 'x',
}
}
}
},
plugins:[{
id: "zoomRangeSlider",
afterDatasetsDraw(chart, args, plugins) {
var chartMin = chart.data.labels[0],
chartMax = chart.data.labels[chart.data.labels.length - 1],
currentZoom = chart.options.scales.x.max - chart.options.scales.x.min,
minMaxDiff = chartMax - chartMin,
posX;
const {
ctx,
chartArea: {
left,
top,
bottom,
right,
width
},
} = chart;
var rectWidth = (currentZoom / minMaxDiff) * width;
if(rectWidth < 7) {
rectWidth = 7;
}
posX = (chart.options.scales.x.min - chartMin) / minMaxDiff;
rectPosition = (posX * (width)) + (rectWidth / 2) + left;
if (rectPosition < left + rectWidth / 2) {
rectPosition = left + rectWidth / 2
}
// Scrollbar background
ctx.beginPath();
ctx.fillStyle = "#f0f0f0";
ctx.rect(left, bottom + 40, width, 15);
ctx.fill();
// Scrollbar
ctx.beginPath();
ctx.fillStyle = "#cdcdcd";
ctx.rect(rectPosition - (rectWidth / 2), bottom + 40, rectWidth, 13);
ctx.fill();
},
afterEvent(chart, args, plugins) {
var chartMin = chart.data.labels[0],
chartMax = chart.data.labels[chart.data.labels.length - 1],
currentZoom = chart.options.scales.x.max - chart.options.scales.x.min,
step = chart.data.labels[1] - chart.data.labels[0],
minMaxDiff = chartMax - chartMin;
const {
ctx,
canvas,
chartArea: {
left,
top,
bottom,
right,
width
},
} = chart;
if (args.event.type === "mousemove" && isDragging === true && args.event.y > 1.05 * bottom) {
// Check where the mouse is
var xPosition = parseFloat(((args.event.x-left) / (right-left)).toFixed(2));
xPosition = xPosition > 1 ? 1 : xPosition < 0 ? 0 : xPosition;
var min = chartMin + (xPosition * minMaxDiff) - (0.5 * currentZoom) - (chartMin + (xPosition * minMaxDiff) - (0.5 * currentZoom)) % step,
max = min + currentZoom;
if (max >= chartMax) {
max = chartMax;
min = max - currentZoom;
} else if (min < chartMin) {
min = chartMin;
max = min + currentZoom;
}
chart.options.scales.x.min = min;
chart.options.scales.x.max = max;
args.changed = true;
// Synchronize the grid and the chart
chart.update('none');
}
},
}]
};
const myChart = new Chart(ctx, cfg);
canvas.onmousedown = function(){
isDragging = true;
};
canvas.onmouseup = function(){
isDragging = false;
};
function zoomout(){
myChart.options.scales.x.min = undefined;
myChart.options.scales.x.max = undefined;
myChart.update('none');
}
.container {
height: 400px;
}
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.3.0/dist/chart.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/hammerjs@2.0.8"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@2.0.1/dist/chartjs-plugin-zoom.min.js "></script>
<div class="container">
<button onclick="zoomout()">Zoom out</button>
<canvas id='myCanvas'></canvas>
</div>