Reputation: 27
I am using chart JS plugin in my react application and I need to select some area in chart and add annotation to that part. For annotation, I use chartjs-plugin-annotation and for area selection I couldn't find something better than chartjs-plugin-zoom's drag function. After drag end, I can get info about start and end points and add some annotation, but chart is immediately zoomed.
I can not find how to prevent zoom after drag selection. Is there any function to prevent zoom or can someone suggest other method to select aren in chart?
Upvotes: 1
Views: 1144
Reputation: 121
It's a bit hacky but if you're wanting to have the drag functionality without zoom why not just set the threshold? The docs don't explicitly describe it, but setting the threshold to 1px wider than the canvas worked for me.
< EDIT > Updated code after Tigran provided input. Added an eventListener on the Chart object for pointerdown and pointerup. Doing so we are able to grab the xAxes label and y value on nearest event position. </ EDIT >
Working Codepen: https://codepen.io/vpolston/pen/KKRmOeO
JS
const data = [22,52,23,66,12,62,86,94,23,52,22,52];
const labels = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul","Aug", "Sep", "Oct", "Nov", "Dec"];
const ctx = document.getElementById('myChart').getContext('2d');
const myChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'myData',
data: data
}]
},
options: {
plugins: {
zoom: {
zoom: {
drag: {
enabled: true,
threshold: 801, // set threshold as described in docs: https://www.chartjs.org/chartjs-plugin-zoom/latest/guide/options.html#drag-options
},
mode: 'x',
}
}
}
}
});
/*
add event listeners for pointerdown and pounterup
using if statements to ensure only events within chartArea are valid
const start_label/end_label = the value of the xAxes label
const start_y_value/end_y_value = the value of the y cordinate for that event
*/
myChart.canvas.addEventListener('pointerdown', event => {
if(event.offsetX >= myChart.chartArea.left){
const datas = myChart.getElementsAtEventForMode(event, 'index', {
intersect: false
});
const start_y_value = datas[0].element.$context.parsed.y;
const x_index = myChart.scales.x.getValueForPixel(event.offsetX);
const start_label = myChart.data.labels[x_index];
document.getElementById("startVals").innerHTML = start_label + ' ' + start_y_value;
}
});
myChart.canvas.addEventListener('pointerup', event => {
if(event.offsetX <= myChart.chartArea.right){
const datas = myChart.getElementsAtEventForMode(event, 'index', {
intersect: false
});
const end_y_value = datas[0].element.$context.parsed.y;
const x_index = myChart.scales.x.getValueForPixel(event.offsetX);
const end_label = myChart.data.labels[x_index];
document.getElementById("endVals").innerHTML = end_label + ' ' + end_y_value;
}
});
HTML
<!-- Canvas -->
<div class="chartContainer">
<canvas id="myChart"></canvas>
</div>
<p><strong>Start axes label & value: </strong><span id="startVals"></span></p>
<p><strong>End axes label & value: </strong><span id="endVals"></span></p>
<!-- include Chart.js 3.9.1 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.js" integrity="sha512-d6nObkPJgV791iTGuBoVC9Aa2iecqzJRE0Jiqvk85BhLHAPhWqkuBiQb1xz2jvuHNqHLYoN3ymPfpiB1o+Zgpw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- include chartjs-plugin-zoom -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-zoom/1.2.1/chartjs-plugin-zoom.js" integrity="sha512-7X7B4dUsqfSxUe5m8NELendyUKx+xwZg4wSFECgBIPGaMSLS6e6oDGkxfJsFOlPADqIwkrP/pI9PihypuWFbEw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
CSS
.chartContainer {
width: 800px;
height: 400px;
}
Alternatively you go a bit more advanced by not using the plugin and doing a custom overlay canvas like Jony outlined here https://stackoverflow.com/a/46282218/9306152.
Hopefully that helps. If so please mark answered :)
Upvotes: 3