Home > Enterprise >  Draggable HTML div doesn't stay in place when zooming in/out. How can I fix this?
Draggable HTML div doesn't stay in place when zooming in/out. How can I fix this?

Time:04-25

I'm trying to create a basic color picker that changes the background color of the page when I drag a marker on the canvas gradient.

The program behaves as expected with what I have so far. But, after dragging it, the marker doesn't stay in place when I zoom in or out on the browser.

I tried adding a div as a wrapper with relative position for the color picker canvas and the marker. This prevented the marker from picking the colors on the canvas, so I removed it.

I also tried appending the marker to the canvas instead of the container, but then it disappears.

const container = document.querySelector('.container');

const colorPickerCanvas = document.querySelector('.colorPickerCanvas');

const colorSlider = document.querySelector('.colorSlider');

const marker = document.createElement('div');
marker.classList.add('marker');
marker.setAttribute('draggable', true);
container.appendChild(marker)


//ADD 2D CONTEXT TO COLOR PICKER CANVAS
let colorPickerCtx = colorPickerCanvas.getContext('2d');
var color = 'blue';

var dragging = false;

//CREATE A HORIZONTAL GRADIENT ON THE CANVAS
let horizontalGradient = colorPickerCtx.createLinearGradient(0, 0, 300, 0);

horizontalGradient.addColorStop(0, 'white');
horizontalGradient.addColorStop(1, color);
colorPickerCtx.fillStyle = horizontalGradient;
colorPickerCtx.fillRect(0, 0, 300, 300);

//CREATE A VERTICAL GRADIENT ON THE CANVAS
let verticalGradient = colorPickerCtx.createLinearGradient(0, 0, 0, 300);

verticalGradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
verticalGradient.addColorStop(1, 'black');
colorPickerCtx.fillStyle = verticalGradient;
colorPickerCtx.fillRect(0, 0, 300, 300);

colorPickerCanvas.addEventListener('click', (event) => {
  event.preventDefault()
  //GET THE COORDINATES OF CLICKED PIXEL
  let xCoordinates = event.pageX - colorPickerCanvas.offsetLeft;
  let yCoordinates = event.pageY - colorPickerCanvas.offsetTop;
  console.log('X coordinates are: '   xCoordinates   ' and Y coordinates are: '   yCoordinates);

  //GET RGB VALUES OF CLICKED PIXEL
  let imgData = colorPickerCtx.getImageData(xCoordinates, yCoordinates, 1, 1);
  ctxR = imgData.data[0];
  ctxG = imgData.data[1];
  ctxB = imgData.data[2];
  console.log('Blue value is: '   ctxB)

  //PLACE MARKER WHERE MOUSE IS CLICKED ON CANVAS
  marker.style.top =  event.pageY - 15   'px';
  marker.style.left = event.pageX - 15   'px';

  document.body.style.backgroundColor = `rgb(${ctxR}, ${ctxG}, ${ctxB})`
});


marker.addEventListener('dragstart', () => {
    dragging = true;
});

colorPickerCanvas.addEventListener('dragover', (event) => {
    if (dragging) {
        //GET COORDINATES OF MARKER WHILE BEING DRAGGED
        let xCoordinates = event.pageX - colorPickerCanvas.offsetLeft;
        let yCoordinates = event.pageY - colorPickerCanvas.offsetTop;

        //CHANGE THE BACKGROUND COLOR WHILE MARKER IS DRAGGED
        let imgData = colorPickerCtx.getImageData(xCoordinates, yCoordinates, 1, 1);
        ctxR = imgData.data[0];
        ctxG = imgData.data[1];
        ctxB = imgData.data[2];

        document.body.style.backgroundColor = `rgb(${ctxR}, ${ctxG}, ${ctxB})`
    };
});

marker.addEventListener('dragend', (event) => {
    dragging = false;
    //DROP THE MARKER WHERE DRAGGING STOPS
    marker.style.top =  event.pageY - 8   'px';
    marker.style.left = event.pageX - 8   'px';
})


//ADD 2D CONTEXT TO COLOR SLIDER
let colorSliderCtx = colorSlider.getContext('2d');

//CREATE A VERTICAL GRADIENT ON THE COLOR SLIDER
let sliderGradient = colorSliderCtx.createLinearGradient(0, 0, 0, 300);
sliderGradient.addColorStop(0, 'red');
sliderGradient.addColorStop(0.1, 'orange');
sliderGradient.addColorStop(0.2, 'yellow');
sliderGradient.addColorStop(0.4, 'lime');
sliderGradient.addColorStop(0.5, 'skyblue');
sliderGradient.addColorStop(0.7, 'blue');
sliderGradient.addColorStop(0.9, 'magenta');
sliderGradient.addColorStop(1, 'red');
colorSliderCtx.fillStyle = sliderGradient;
colorSliderCtx.fillRect(0, 0, 40, 300);

colorSlider.addEventListener('click', (event) => {

  //GET THE COORDINATES OF CLICKED PIXEL
  let sliderX = event.pageX - colorSlider.offsetLeft;
  let sliderY = event.pageY - colorSlider.offsetTop;
  console.log(sliderX)

  //GET RGB VALUES OF CLICKED PIXEL
  let sliderImgData = colorSliderCtx.getImageData(sliderX, sliderY, 1, 1);
  sliderR = sliderImgData.data[0];
  sliderG = sliderImgData.data[1];
  sliderB = sliderImgData.data[2];
  let color = `rgb(${sliderR}, ${sliderG}, ${sliderB})`

  //CREATE A HORIZONTAL GRADIENT ON THE CANVAS
  let horizontalGradient = colorPickerCtx.createLinearGradient(0, 0, 300, 0);
  
  horizontalGradient.addColorStop(0, 'white');
  horizontalGradient.addColorStop(1, color);
  colorPickerCtx.fillStyle = horizontalGradient;
  colorPickerCtx.fillRect(0, 0, 300, 300);

  //CREATE A VERTICAL GRADIENT ON THE CANVAS
  let verticalGradient = colorPickerCtx.createLinearGradient(0, 0, 0, 300);

  verticalGradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
  verticalGradient.addColorStop(1, 'black');
  colorPickerCtx.fillStyle = verticalGradient;
  colorPickerCtx.fillRect(0, 0, 300, 300);

  //PLACE MARKER WHERE MOUSE IS CLICKED ON SLIDER

});
.container {
    display: flex;
    justify-content: center;
    gap: 16px;
    position: relative;
  }

.colorPickerCanvas {
    position: relative;
    align-self: flex-start;
}

.marker {
    position: absolute; 
    border-radius: 50%;
    background-color: rgba(255, 255, 255, 0);
    border-style: solid;
    width: 8px;
    height: 8px;
}

.colorSlider {
    align-self: flex-start;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Color Picker</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div >
        <canvas width="300px" height="300px" ></canvas>
        <canvas width="40px" height="300px" ></canvas>
    </div>  
    <script src='script.js'></script>
</body>
</html>

I'll appreciate any feedback or constructive criticism on other aspects of my code as well!

CodePudding user response:

Adding the styling max-width: 400px; onto body worked:

const container = document.querySelector('.container');

const colorPickerCanvas = document.querySelector('.colorPickerCanvas');

const colorSlider = document.querySelector('.colorSlider');

const marker = document.createElement('div');
marker.classList.add('marker');
marker.setAttribute('draggable', true);
container.appendChild(marker)


//ADD 2D CONTEXT TO COLOR PICKER CANVAS
let colorPickerCtx = colorPickerCanvas.getContext('2d');
var color = 'blue';

var dragging = false;

//CREATE A HORIZONTAL GRADIENT ON THE CANVAS
let horizontalGradient = colorPickerCtx.createLinearGradient(0, 0, 300, 0);

horizontalGradient.addColorStop(0, 'white');
horizontalGradient.addColorStop(1, color);
colorPickerCtx.fillStyle = horizontalGradient;
colorPickerCtx.fillRect(0, 0, 300, 300);

//CREATE A VERTICAL GRADIENT ON THE CANVAS
let verticalGradient = colorPickerCtx.createLinearGradient(0, 0, 0, 300);

verticalGradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
verticalGradient.addColorStop(1, 'black');
colorPickerCtx.fillStyle = verticalGradient;
colorPickerCtx.fillRect(0, 0, 300, 300);

colorPickerCanvas.addEventListener('click', (event) => {
  event.preventDefault()
  //GET THE COORDINATES OF CLICKED PIXEL
  let xCoordinates = event.pageX - colorPickerCanvas.offsetLeft;
  let yCoordinates = event.pageY - colorPickerCanvas.offsetTop;
  console.log('X coordinates are: '   xCoordinates   ' and Y coordinates are: '   yCoordinates);

  //GET RGB VALUES OF CLICKED PIXEL
  let imgData = colorPickerCtx.getImageData(xCoordinates, yCoordinates, 1, 1);
  ctxR = imgData.data[0];
  ctxG = imgData.data[1];
  ctxB = imgData.data[2];
  console.log('Blue value is: '   ctxB)

  //PLACE MARKER WHERE MOUSE IS CLICKED ON CANVAS
  marker.style.top =  event.pageY - 15   'px';
  marker.style.left = event.pageX - 15   'px';

  document.body.style.backgroundColor = `rgb(${ctxR}, ${ctxG}, ${ctxB})`
});


marker.addEventListener('dragstart', () => {
    dragging = true;
});

colorPickerCanvas.addEventListener('dragover', (event) => {
    if (dragging) {
        //GET COORDINATES OF MARKER WHILE BEING DRAGGED
        let xCoordinates = event.pageX - colorPickerCanvas.offsetLeft;
        let yCoordinates = event.pageY - colorPickerCanvas.offsetTop;

        //CHANGE THE BACKGROUND COLOR WHILE MARKER IS DRAGGED
        let imgData = colorPickerCtx.getImageData(xCoordinates, yCoordinates, 1, 1);
        ctxR = imgData.data[0];
        ctxG = imgData.data[1];
        ctxB = imgData.data[2];

        document.body.style.backgroundColor = `rgb(${ctxR}, ${ctxG}, ${ctxB})`
    };
});

marker.addEventListener('dragend', (event) => {
    dragging = false;
    //DROP THE MARKER WHERE DRAGGING STOPS
    marker.style.top =  event.pageY - 8   'px';
    marker.style.left = event.pageX - 8   'px';
})


//ADD 2D CONTEXT TO COLOR SLIDER
let colorSliderCtx = colorSlider.getContext('2d');

//CREATE A VERTICAL GRADIENT ON THE COLOR SLIDER
let sliderGradient = colorSliderCtx.createLinearGradient(0, 0, 0, 300);
sliderGradient.addColorStop(0, 'red');
sliderGradient.addColorStop(0.1, 'orange');
sliderGradient.addColorStop(0.2, 'yellow');
sliderGradient.addColorStop(0.4, 'lime');
sliderGradient.addColorStop(0.5, 'skyblue');
sliderGradient.addColorStop(0.7, 'blue');
sliderGradient.addColorStop(0.9, 'magenta');
sliderGradient.addColorStop(1, 'red');
colorSliderCtx.fillStyle = sliderGradient;
colorSliderCtx.fillRect(0, 0, 40, 300);

colorSlider.addEventListener('click', (event) => {

  //GET THE COORDINATES OF CLICKED PIXEL
  let sliderX = event.pageX - colorSlider.offsetLeft;
  let sliderY = event.pageY - colorSlider.offsetTop;
  console.log(sliderX)

  //GET RGB VALUES OF CLICKED PIXEL
  let sliderImgData = colorSliderCtx.getImageData(sliderX, sliderY, 1, 1);
  sliderR = sliderImgData.data[0];
  sliderG = sliderImgData.data[1];
  sliderB = sliderImgData.data[2];
  let color = `rgb(${sliderR}, ${sliderG}, ${sliderB})`

  //CREATE A HORIZONTAL GRADIENT ON THE CANVAS
  let horizontalGradient = colorPickerCtx.createLinearGradient(0, 0, 300, 0);
  
  horizontalGradient.addColorStop(0, 'white');
  horizontalGradient.addColorStop(1, color);
  colorPickerCtx.fillStyle = horizontalGradient;
  colorPickerCtx.fillRect(0, 0, 300, 300);

  //CREATE A VERTICAL GRADIENT ON THE CANVAS
  let verticalGradient = colorPickerCtx.createLinearGradient(0, 0, 0, 300);

  verticalGradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
  verticalGradient.addColorStop(1, 'black');
  colorPickerCtx.fillStyle = verticalGradient;
  colorPickerCtx.fillRect(0, 0, 300, 300);

  //PLACE MARKER WHERE MOUSE IS CLICKED ON SLIDER

});
body {
  max-width: 400px;  
}

.container {
    display: flex;
    justify-content: center;
    gap: 16px;
    position: relative;
  }

.colorPickerCanvas {
    position: relative;
    align-self: flex-start;
}


.marker {
    position: absolute; 
    border-radius: 50%;
    background-color: rgba(255, 255, 255, 0);
    border-style: solid;
    width: 8px;
    height: 8px;
}

.colorSlider {
    align-self: flex-start;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Color Picker</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div >
        <canvas width="300px" height="300px" ></canvas>
        <canvas width="40px" height="300px" ></canvas>
    </div>  
    <script src='script.js'></script>
</body>
</html>

  • Related