To give a color picker the expected behavior, there are some tricks to how you set up the interaction between the gradient canvas and the user’s mouse. The main thing is that while the user is holding down click and moving the selector around, the action should still function while the user’s mouse is outside the canvas area.

The key is instead of adding the event listeners to the canvas, add the listeners for mousemove and mouseup to the window.

//canvas listeners"mousedown", (e) => {
window.addEventListener("mousemove", (e) => {
window.addEventListener("mouseup", (e) => {

Of course, now we need to do some calculations to match the user’s mouse position with coordinates on the canvas.

On mousedown, the values are easy to get since the focus is already on the canvas:

handleMouseDown(e) {
this.clicked = true;
this.selectSL(e.offsetX, e.offsetY);

The offset coordinates are what we’ll need to calculate with mousemove. First, you can find the location of the canvas within the window with the following lines:

let canvasXOffset = - document.getElementsByTagName("html")[0].getBoundingClientRect().left;let canvasYOffset = - document.getElementsByTagName("html")[0].getBoundingClientRect().top;

Taking the difference of that value from the page coordinates of the window gives the values we want:

handleMouseMove(e) {
if (this.clicked) {
let canvasXOffset = - document.getElementsByTagName("html")[0].getBoundingClientRect().left;
let canvasYOffset = - document.getElementsByTagName("html")[0].getBoundingClientRect().top; let canvasX = e.pageX - canvasXOffset;
let canvasY = e.pageY - canvasYOffset;
//constrain coordinates
if (canvasX > this.width) { canvasX = this.width }
if (canvasX < 0) { canvasX = 0 }
if (canvasY > this.height) { canvasY = this.height }
if (canvasY < 0) { canvasY = 0 }
this.selectSL(canvasX, canvasY);

By constraining the coordinates to not allow values outside the canvas, the user can easily drag the selector along the edges of the canvas.