I’m wanting to implement canvas as background for my website so users can use their cursors to paint on the webpage like this codepen: https://codepen.io/cocotx/pen/PoGRdxQ?editors=1010 (this is an example code from http://www.dgp.toronto.edu/~clwen/test/canvas-paint-tutorial/)
if(window.addEventListener) { window.addEventListener('load', function () { var canvas, context; // Initialization sequence. function init () { // Find the canvas element. canvas = document.getElementById('imageView'); if (!canvas) { alert('Error: I cannot find the canvas element!'); return; } if (!canvas.getContext) { alert('Error: no canvas.getContext!'); return; } // Get the 2D canvas context. context = canvas.getContext('2d'); if (!context) { alert('Error: failed to getContext!'); return; } // Attach the mousemove event handler. canvas.addEventListener('mousemove', ev_mousemove, false); } // The mousemove event handler. var started = false; function ev_mousemove (ev) { var x, y; // Get the mouse position relative to the canvas element. if (ev.layerX || ev.layerX == 0) { // Firefox x = ev.layerX; y = ev.layerY; } else if (ev.offsetX || ev.offsetX == 0) { // Opera x = ev.offsetX; y = ev.offsetY; } // The event handler works like a drawing pencil which tracks the mouse // movements. We start drawing a path made up of lines. if (!started) { context.beginPath(); context.moveTo(x, y); started = true; } else { context.lineTo(x, y); context.stroke(); } } init(); }, false); }
The problem is that the cursor stops painting when I scroll until I move my mouse again. Any idea on how to keep cursor painting even when I scroll?
Thanks in advance! Much appreciated!
Advertisement
Answer
You will have to store the last mouse event and fire a new fake one in the scroll event.
Luckily, the MouseEvent constructor accepts an mouseEventInit object on which we can set the clientX
and clientY
values of our new event, so we just need to store these values from the previous event and dispatch it in the scroll
event.
Now, I couldn’t help but rewrite almost everything from your code.
It had a lot of checks for old browsers (like very old ones which should never face the web again anyway), you may want to add it again if you wish.
It wasn’t clearing the context, meaning that every time it drew a new line, it also did draw again the previous lines over themselves, leading in fatter lines, with a lot of noise at the beginning, and smoother lines at the end.
This could be fixed in many ways, the less intrusive one was to just clear the context at each frame.
To get the relative mouse position, it now uses the clientX and clientY properties of the event.
And the rest of the changes is commented in the snippet.
window.addEventListener('load', function () { const canvas = document.getElementById('imageView'); context = canvas.getContext("2d"); let last_event; // we will store our mouseevents here // we now listen to the mousemove event on the document, // not only on the canvas document.addEventListener('mousemove', ev_mousemove); document.addEventListener('scroll', fireLastMouseEvent, { capture: true } ); // to get the initial position of the cursor // even if the mouse never moves // we listen to a single mouseenter event on the document's root element // unfortunately this seems to not work in Chrome document.documentElement.addEventListener( "mouseenter", ev_mousemove, { once: true } ); // called in scroll event function fireLastMouseEvent() { if( last_event ) { // fire a new event on the document using the same clientX and clientY values document.dispatchEvent( new MouseEvent( "mousemove", last_event ) ); } } // mousemove event handler. function ev_mousemove (ev) { const previous_evt = last_event || {}; const was_offscreen = previous_evt.offscreen; // only for "true" mouse event if( ev.isTrusted ) { // store the clientX and clientY props in an object const { clientX, clientY } = ev; last_event = { clientX, clientY }; } // get the relative x and y positions from the mouse event const point = getRelativePointFromEvent( ev, canvas ); // check if we are out of the canvas viewPort if( point.x < 0 || point.y < 0 || point.x > canvas.width || point.y > canvas.height ) { // remember we were last_event.offscreen = true; // if we were already, don't draw if( was_offscreen ) { return; } } // we come from out-of-screen to in-screen else if( was_offscreen ) { // move to the previous point recorded as out-of-screen const previous_point = getRelativePointFromEvent( previous_evt, canvas ); context.moveTo( previous_point.x, previous_point.y ); } // add the new point to the context's sub-path definition context.lineTo( point.x, point.y ); // clear the previous drawings context.clearRect( 0, 0, canvas.width, canvas.height ); // draw everything again context.stroke(); } function getRelativePointFromEvent( ev, elem ) { // first find the bounding rect of the element const bbox = elem.getBoundingClientRect(); // subtract the bounding rect from the client coords const x = ev.clientX - bbox.left; const y = ev.clientY - bbox.top; return { x, y }; } });
#container { width: 400px; height: 200px; overflow: auto; border: 1px solid; } #imageView { border: 1px solid #000; } canvas { margin: 100px; }
<div id="container"> <canvas id="imageView" width="400" height="300"></canvas> </div>