Description
When pan.modifierKey: 'ctrl' and zoom.drag.enabled: true are both configured, there is a race condition when switching quickly between the two interactions.
If the user:
- Pans the chart by holding Ctrl + drag
- Releases Ctrl
- Immediately starts a new drag (without Ctrl) to trigger drag-zoom
...the new drag pans the chart, but it is still showing the drag-zoom selection box. The plugin appears to still be in pan mode for a short period after Ctrl is released. If the user waits 1–2 seconds between releasing Ctrl and starting the next drag, the drag-zoom works correctly.
This only happens when the two interactions are performed in quick succession. It is a timing/state issue — the plugin does not fully exit pan mode before the next mousedown event is processed.
Steps to reproduce
- Open the minimal example below.
- Hold Ctrl and drag to pan the chart.
- Immediately release Ctrl and drag again (without Ctrl) to zoom.
- Observe: the chart pans again instead of showing the drag-zoom selection area.
Expected: releasing Ctrl and dragging should immediately trigger drag-zoom with the blue selection box.
Actual: the chart pans again. The blue selection box only appears if you wait ~1–2 seconds after releasing Ctrl.
Environment
- chartjs-plugin-zoom: 2.0.1
- chart.js: 4.4.0
- chartjs-adapter-date-fns: 3.0.0
- hammerjs: 2.0.8
- Browser: Chrome / Firefox (reproducible in both)
Minimal reproducible example
Paste into a single HTML file and open in browser:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/hammerjs@2.0.8/hammer.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@2.0.1/dist/chartjs-plugin-zoom.min.js"></script>
</head>
<body style="font-family: sans-serif; padding: 1rem;">
<p>
1. Hold <b>Ctrl + drag</b> to pan.<br>
2. Release Ctrl and <b>immediately drag</b> (no Ctrl) to zoom.<br>
Bug: step 2 pans instead of showing the zoom selection box.<br>
If you <b>wait 1–2 seconds</b> before step 2, it works correctly.
</p>
<canvas id="chart" style="max-height: 400px;"></canvas>
<script>
const now = Date.now();
new Chart(document.getElementById('chart'), {
type: 'line',
data: {
labels: Array.from({ length: 12 }, (_, i) => now + i * 1000),
datasets: [
{
label: 'Series A',
data: [1, 3, 2, 5, 4, 7, 6, 8, 5, 9, 4, 6],
borderColor: 'steelblue',
tension: 0.3,
pointRadius: 4
},
{
label: 'Series B',
data: [4, 2, 6, 3, 7, 2, 8, 3, 7, 4, 8, 3],
borderColor: 'tomato',
tension: 0.3,
pointRadius: 4
}
]
},
options: {
plugins: {
zoom: {
zoom: {
wheel: { enabled: true },
drag: {
enabled: true,
borderColor: 'rgb(54, 162, 235)',
borderWidth: 1,
backgroundColor: 'rgba(54, 162, 235, 0.3)',
threshold: 5
},
mode: 'xy',
scaleMode: 'xy'
},
pan: {
enabled: true,
mode: 'xy',
modifierKey: 'ctrl'
}
}
},
scales: {
x: { type: 'time' },
y: { grace: '25%' }
}
}
});
</script>
</body>
</html>
Notes
The root cause appears to be that the plugin's internal pan state (driven by Hammer.js) is not reset synchronously when the Ctrl key is released. The modifierKey check happens at the start of a gesture, but Hammer.js gesture recognizers have their own lifecycle and may still be in a "possible" or "began" state from the previous pan gesture when the next mousedown fires.
A workaround — dynamically toggling zoom.drag.enabled and pan.enabled on mousedown (capture phase) depending on e.ctrlKey — resolves the issue, but requires significant boilerplate. A proper fix in the plugin would be to ensure pan state and Hammer recognizer state are fully reset as soon as modifierKey is no longer held.
Description
When
pan.modifierKey: 'ctrl'andzoom.drag.enabled: trueare both configured, there is a race condition when switching quickly between the two interactions.If the user:
...the new drag pans the chart, but it is still showing the drag-zoom selection box. The plugin appears to still be in pan mode for a short period after Ctrl is released. If the user waits 1–2 seconds between releasing Ctrl and starting the next drag, the drag-zoom works correctly.
This only happens when the two interactions are performed in quick succession. It is a timing/state issue — the plugin does not fully exit pan mode before the next
mousedownevent is processed.Steps to reproduce
Expected: releasing Ctrl and dragging should immediately trigger drag-zoom with the blue selection box.
Actual: the chart pans again. The blue selection box only appears if you wait ~1–2 seconds after releasing Ctrl.
Environment
Minimal reproducible example
Paste into a single HTML file and open in browser:
Notes
The root cause appears to be that the plugin's internal pan state (driven by Hammer.js) is not reset synchronously when the Ctrl key is released. The
modifierKeycheck happens at the start of a gesture, but Hammer.js gesture recognizers have their own lifecycle and may still be in a "possible" or "began" state from the previous pan gesture when the nextmousedownfires.A workaround — dynamically toggling
zoom.drag.enabledandpan.enabledonmousedown(capture phase) depending one.ctrlKey— resolves the issue, but requires significant boilerplate. A proper fix in the plugin would be to ensure pan state and Hammer recognizer state are fully reset as soon asmodifierKeyis no longer held.