I'm working on a D3 application and have bumped into a strange zooming issue that occurs in Chrome but not Firefox (these are the only browsers I've tested my code in). I've boiled the problem down to the code snippet below.
Basically, in certain conditions, the zoom handler I have defined does not get called when I scroll over my canvas element (using either my mouse's scroll button or two fingers on my laptop's trackpad). However, clicking and dragging on the canvas to try to pan it does successfully invoke the zoom handler.
Details of the issue:
Zooming works as expected as long as the "click" event handler does not get called. Once it's called, scroll zooming will not work. And it has something to do with invoking
ctx.getImageDatain the click event handler.If I omit the
.callat the end of the canvas selection that invokesclearRect, the zooming issue occurs even if you did not trigger a "click" eventThe issue occurs in other versions of D3 besides v5. I've tried v6 and v7.
The Chrome version I'm using is 92.0.4515.159
There are a couple ways I know of to resolve the issue:
- The issue is resolved if the canvas element's opacity is not set to 0.
- It can also be resolved by calling
window.addEventListener('wheel', () => {}) - From what I recall, another solution is defining the zoom behavior on a parent element of the canvas instead.
The code below creates a canvas element in the top left corner (it's not visible since its opacity is 0). Zooming on the element will print a "zooming" message to the console. Clicking on the canvas will print "click". You'll find that with the code as is, if you click on the element and then try to scroll zoom, "zooming" will not get printed. You can also run the code in JSFiddle: https://jsfiddle.net/yr3jdvhw/37/
I'd like to know what's causing this issue. As I mentioned, I could just set the canvas's opacity to a non-zero value to avoid the problem. But I'd really like to know the root cause.
<html>
<head>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<canvas class="my-canvas"></canvas>
<script>
const myCanvas = d3.select(".my-canvas")
.style('opacity', 0)
.on('click', function () {
console.log("click")
let ctx = this.getContext('2d')
const pixel = ctx.getImageData(0, 0, 1, 1)
})
.call(d3.zoom().on('zoom', () => {
console.log("zooming")
}))
.call(function (s) {
let ctx = s.node().getContext('2d')
ctx.clearRect(0, 0, s.node().width, s.node().height)
})
</script>
</body>
</html>