score:40
Building on @jordanwillis's and your answers, you can easily achieve anything you want, by placing another canvas on top on your chart.
Just add pointer-events:none
to it's style to make sure it doesn't intefere with the chart's events.
No need to use the annotations plugin.
For example (in this example canvas
is the original chart canvas and overlay
is your new canvas placed on top):
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderWidth: 1
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderWidth: 1
}
]
},
options: {
scales: {
yAxes: [{
ticks: {
reverse: false
}
}]
}
}
}
var canvas = document.getElementById('chartJSContainer');
var ctx = canvas.getContext('2d');
var chart = new Chart(ctx, options);
var overlay = document.getElementById('overlay');
var startIndex = 0;
overlay.width = canvas.width;
overlay.height = canvas.height;
var selectionContext = overlay.getContext('2d');
var selectionRect = {
w: 0,
startX: 0,
startY: 0
};
var drag = false;
canvas.addEventListener('pointerdown', evt => {
const points = chart.getElementsAtEventForMode(evt, 'index', {
intersect: false
});
startIndex = points[0]._index;
const rect = canvas.getBoundingClientRect();
selectionRect.startX = evt.clientX - rect.left;
selectionRect.startY = chart.chartArea.top;
drag = true;
// save points[0]._index for filtering
});
canvas.addEventListener('pointermove', evt => {
const rect = canvas.getBoundingClientRect();
if (drag) {
const rect = canvas.getBoundingClientRect();
selectionRect.w = (evt.clientX - rect.left) - selectionRect.startX;
selectionContext.globalAlpha = 0.5;
selectionContext.clearRect(0, 0, canvas.width, canvas.height);
selectionContext.fillRect(selectionRect.startX,
selectionRect.startY,
selectionRect.w,
chart.chartArea.bottom - chart.chartArea.top);
} else {
selectionContext.clearRect(0, 0, canvas.width, canvas.height);
var x = evt.clientX - rect.left;
if (x > chart.chartArea.left) {
selectionContext.fillRect(x,
chart.chartArea.top,
1,
chart.chartArea.bottom - chart.chartArea.top);
}
}
});
canvas.addEventListener('pointerup', evt => {
const points = chart.getElementsAtEventForMode(evt, 'index', {
intersect: false
});
drag = false;
console.log('implement filter between ' + options.data.labels[startIndex] + ' and ' + options.data.labels[points[0]._index]);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.0/Chart.js"></script>
<body>
<canvas id="overlay" width="600" height="400" style="position:absolute;pointer-events:none;"></canvas>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
</body>
Notice we're basing our events and coordinates on the original canvas, but we draw on the overlay. This way we don't mess the chart's functionality.
score:0
The people who made ChartJS also made a plugin called chartjs-plugin-zoom. To install the plugin type:
npm install chartjs-plugin-zoom.
Implement:
import { Chart } from 'chart.js';
import zoomPlugin from 'chartjs-plugin-zoom';
Chart.register(zoomPlugin);
To add zooming functionality by dragging, add this to the chart configuration:
options: {
plugins: {
zoom: {
pan: {
enabled: true,
mode: 'x',
modifierKey: 'ctrl',
},
zoom: {
drag: {
enabled: true
},
mode: 'x'
}
}
}
}
A more thorough installation and use tutorial can be found here. Instructions on how to implement zooming functionality can be found here.
score:1
Update a few months later based on @jordanwillis' answer: I've got the beginnings of range selection.
canvas.onpointerdown = function (evt) {
clearAnnotations()
const points = chart.getElementsAtEventForMode(evt, 'index', { intersect: false })
const label = chart.data.labels[points[0]._index]
addAnnotation(label)
}
canvas.onpointerup = function (evt) {
const points = chart.getElementsAtEventForMode(evt, 'index', { intersect: false })
const label = chart.data.labels[points[0]._index]
addAnnotation(label)
}
function clearAnnotations () {
if (chart.options.annotation) {
chart.options.annotation.annotations = []
}
}
function addAnnotation (label) {
const annotation = {
scaleID: 'x-axis-0',
type: 'line',
mode: 'vertical',
value: label,
borderColor: 'red'
}
chart.options.annotation = chart.options.annotation || {}
chart.options.annotation.annotations = chart.options.annotation.annotations || []
chart.options.annotation.annotations.push(annotation)
chart.update()
}
Still need to figure out how to show a visual hover indicator as in the demo linked in the question, but it's a start.
score:2
Unfortunately, nothing like this is built into chart.js. You would have to implement your own event hooks and handlers that would render a highlighted section on a chart and then use the .getElementsAtEvent(e)
prototype method to figure out what data has been highlighted. Even these hooks that are built in may not be enough to implement what you are wanting.
Event hook options are:
Add event handlers on the canvas element itself (see example below)
canvas.onclick = function(evt){ var activePoints = myLineChart.getElementsAtEvent(evt); // => activePoints is an array of points on the canvas that are at the same position as the click event. };
Add event handler on the chart.js chart object using the
onClick
config option (explained here).Extend some of the core charts event hooks and add your own. (see here for some guidance).
Assuming this approach works, then you could then filter your original chart data array accordingly (in the underlying chart.js object) and call the .update()
prototype method to paint a new chart.
score:3
For all of you interested in Jony Adamits solution, I created a ChartJs plugin based on his implementation. Additionaly I fixed some minor issues in regard to resizing the chart and detection of the selected data points.
Feel free to use it or to create a plugin github repo for it.
Installation
import "chart.js";
import {Chart} from 'chart.js';
import {ChartJsPluginRangeSelect} from "./chartjs-plugin-range-select";
Chart.pluginService.register(new ChartJsPluginRangeSelect());
Configuration
let chartOptions = rangeSelect: {
onSelectionChanged: (result: Array<Array<any>>) => {
console.log(result);
}
}
Plugin Code
import {Chart, ChartSize, PluginServiceGlobalRegistration, PluginServiceRegistrationOptions} from "chart.js";
interface ChartJsPluginRangeSelectExtendedOptions {
rangeSelect?: RangeSelectOptions;
}
interface RangeSelectOptions {
onSelectionChanged?: (filteredDataSets: Array<Array<any>>) => void;
fillColor?: string | CanvasGradient | CanvasPattern;
cursorColor?: string | CanvasGradient | CanvasPattern;
cursorWidth?: number;
state?: RangeSelectState;
}
interface RangeSelectState {
canvas: HTMLCanvasElement;
}
interface ActiveSelection {
x: number;
w: number;
}
export class ChartJsPluginRangeSelect implements PluginServiceRegistrationOptions, PluginServiceGlobalRegistration {
public id = 'rangeSelect';
beforeInit(chartInstance: Chart, options?: any) {
const opts = (chartInstance.config.options as ChartJsPluginRangeSelectExtendedOptions);
if (opts.rangeSelect) {
const canvas = this.createOverlayCanvas(chartInstance);
opts.rangeSelect = Object.assign({}, opts.rangeSelect, {state: {canvas: canvas}});
chartInstance.canvas.parentElement.prepend(canvas);
}
}
resize(chartInstance: Chart, newChartSize: ChartSize, options?: any) {
const rangeSelectOptions = (chartInstance.config.options as ChartJsPluginRangeSelectExtendedOptions).rangeSelect;
if (rangeSelectOptions) {
rangeSelectOptions.state.canvas.width = newChartSize.width;
rangeSelectOptions.state.canvas.height = newChartSize.height;
}
}
destroy(chartInstance: Chart) {
const rangeSelectOptions = (chartInstance.config.options as ChartJsPluginRangeSelectExtendedOptions).rangeSelect;
if (rangeSelectOptions) {
rangeSelectOptions.state.canvas.remove();
delete rangeSelectOptions.state;
}
}
private createOverlayCanvas(chart: Chart): HTMLCanvasElement {
const rangeSelectOptions = (chart.config.options as ChartJsPluginRangeSelectExtendedOptions).rangeSelect;
const overlay = this.createOverlayHtmlCanvasElement(chart);
const ctx = overlay.getContext('2d');
let selection: ActiveSelection = {x: 0, w: 0};
let isDragging = false;
chart.canvas.addEventListener('pointerdown', evt => {
const rect = chart.canvas.getBoundingClientRect();
selection.x = this.getXInChartArea(evt.clientX - rect.left, chart);
isDragging = true;
});
chart.canvas.addEventListener('pointerleave', evt => {
if (!isDragging) {
ctx.clearRect(0, 0, overlay.width, overlay.height);
}
});
chart.canvas.addEventListener('pointermove', evt => {
ctx.clearRect(0, 0, chart.canvas.width, chart.canvas.height);
const chartContentRect = chart.canvas.getBoundingClientRect();
const currentX = this.getXInChartArea(evt.clientX - chartContentRect.left, chart);
if (isDragging) {
selection.w = currentX - selection.x;
ctx.fillStyle = rangeSelectOptions.fillColor || '#00000044';
ctx.fillRect(selection.x, chart.chartArea.top, selection.w, chart.chartArea.bottom - chart.chartArea.top);
} else {
const cursorWidth = rangeSelectOptions.cursorWidth || 1;
ctx.fillStyle = rangeSelectOptions.cursorColor || '#00000088';
ctx.fillRect(currentX, chart.chartArea.top, cursorWidth, chart.chartArea.bottom - chart.chartArea.top);
}
});
chart.canvas.addEventListener('pointerup', evt => {
const onSelectionChanged = rangeSelectOptions.onSelectionChanged;
if (onSelectionChanged) {
onSelectionChanged(this.getDataSetDataInSelection(selection, chart));
}
selection = {w: 0, x: 0};
isDragging = false;
ctx.clearRect(0, 0, overlay.width, overlay.height);
});
return overlay;
}
private createOverlayHtmlCanvasElement(chartInstance: Chart): HTMLCanvasElement {
const overlay = document.createElement('canvas');
overlay.style.position = 'absolute';
overlay.style.pointerEvents = 'none';
overlay.width = chartInstance.canvas.width;
overlay.height = chartInstance.canvas.height;
return overlay;
}
private getXInChartArea(val: number, chartInstance: Chart) {
return Math.min(Math.max(val, chartInstance.chartArea.left), chartInstance.chartArea.right);
}
private getDataSetDataInSelection(selection: ActiveSelection, chartInstance: Chart): Array<any> {
const result = [];
const xMin = Math.min(selection.x, selection.x + selection.w);
const xMax = Math.max(selection.x, selection.x + selection.w);
for (let i = 0; i < chartInstance.data.datasets.length; i++) {
result[i] = chartInstance.getDatasetMeta(i)
.data
.filter(data => xMin <= data._model.x && xMax >= data._model.x)
.map(data => chartInstance.data.datasets[i].data[data._index]);
}
return result;
}
}
Source: stackoverflow.com
Related Query
- How do I selecting a date range (like onClick but drag/select)
- How do I add Date range filter in chartjs in the context of Vue.js
- How to run Chart.js samples using source code
- How to add OnClick Event on labels in Chart.js v2.0?
- ng2-charts: How to set fixed range for y axis
- How do I change the 'months' language displayed on the date axis in Chart JS?
- Angular chart.js onClick is not working - how to get all elements of a bar when it is clicked on
- How to display date as label on x-axis in chart.js
- How to get onClick Event for a Label in ChartJS and React?
- How do I put this on Real-Time? I already put (async: True) but it doesnt work
- Date range picker and chart js
- How to add ChartJS code in Html2Pdf to view image
- Update Chart JS with date range selector
- How to allow zooming in between range only in chartjs-plugin-zoom
- How to define chart.js time scale date formats
- How to select and pass array object to data setting of chart.js config?
- How can i create a background of moving graphs like chartsjs?
- How to access data outside the onclick function
- How can I achieve displaying data points on chart but Dotted? Chart.js V3.7.0
- How do I add time sourced from an external source as an X axis to a ChartJS graph?
- chart.js how to show tick marks but hide gridlines
- How to get Uptime charts for Web Monitoring like Dynatrace?
- How do I add padding on the right of tooltip, but not the left in Chart.js?
- code works fine on jsfiddle but one function is not working on website
- How do I customize y-axis labels and randomly pick the value from the data range for x-axis in Chart js
- I have 10 points inn x-axis, ranging [-25,-20,,-15,-10,0,10,20,30,40,50]. But I want my line chart to start from -15 of x-axis. How we can achieve?
- How can I show JSON data in Chart.js with Javascript if static values are working but dynamic from mySQL are not?
- How to set a locale for Chart.js date labels?
- How to catch an onClick event on bubble chart?
- How to generate color code dynamically by swapping 2 characters within a string using PHP
More Query from same tag
- place images over pie chart slices using chartjs
- How to align vertical axis area on two charts in chart.js
- Html5 web page with chartjs: loading an external json file and create a line chart
- How to import and use a custom Chart.js plugin in Nuxt? (Chartjs-vuejs v2.9.4)
- Interactive chart via ChartJS
- Chartjs force horizontal bar to start from the lowest value
- how do I make chart.js smaller? (html, chart.js)
- chart.js, after each update, avoid scrolling to top position
- Chart.js data background color is overwriting point background color
- Show multiple data in a single data in chart.js
- Chartjs with Vue, Bar Chart Border Radius not working
- how can I add a django array element into a js array using an if condition
- How can I set a class to a HTML element which is created by custom tooltip function?
- Chart.js 2.2.x - How to access the pixel coordinate of a point in my dataset
- Chart JS display Data Value on the top of the Bar
- Chart.js label color
- "Type of import (".../charjs/types/indexsm" has no construct signatures issue on Vue3 and ChartJS?
- AngularJS Charts not updating data correctly
- chartjs resizing very quickly (flickering) on mouseover
- Set xAxes labels to format YYYY-MM-DD HH:mm:ss in Chart.js
- Export chart and table both to pdf
- Chart.js LineChart how to show only part of dataset and support horizontal scrolling
- How to change background color of labels in line chart from chart.js?
- Vue-Chart.js: a chart doesn't begin from zero
- Chart JS change pointBackgroundColor based on data value
- Create a pie chart with chartjs package in R
- ChartJS v2: how to remove strange line in Line Chart
- JsPDF and chart.js: Increase chart resolution
- Chart.js not working on my page?
- multiple horizontal bar charts to display in same row