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>