Home > Software engineering >  Can I snap this (blue) div on the highlighted square on the canvas?
Can I snap this (blue) div on the highlighted square on the canvas?

Time:06-04

When you drag the blue div, I'd like it to 'snap' into the middle of the green highlighted square when the mouse button is released. I cannot seem to find a way to read the coordinates from the box[] (Path2D()) interface. So is there a simple way to achieve this? Should I save the coordinates of the squares individually? Or can I somehow still get the points out of the Path2D interface?

const board = document.getElementById("board");
const ctxB = board.getContext("2d");
var Grid = false;
const boxsize = 64;
const amountOfrows = 8;
const amountOfHorBoxes = 7;
const totalAmountOfBoxes = amountOfrows * amountOfHorBoxes;
board.width = boxsize * 7.5;
board.height = boxsize * 8;
var addHorBox = 0;
var addVertBox = 0;
let boxes = [];

function drawGrid(){
    Grid=true;
    // for the amout of rows
    for (let rowcount = 0; rowcount < amountOfrows; rowcount  ) {
        ctxB.lineWidth = 1;
        ctxB.strokeStyle = "black";
        ctxB.fillStyle = "white";
        // filling the rows
        if(rowcount%2==0){
            for (let boxcount = 0; boxcount < amountOfHorBoxes; boxcount  ) {
                let box = new Path2D();
                box.rect(addHorBox, addVertBox, boxsize, boxsize);
                boxes.push(box);
                ctxB.fill(box);
                ctxB.stroke(box);
                addHorBox =boxsize;
            }
        }
        addHorBox=0;
        addVertBox =boxsize;
    }
}
MoveUnit(document.getElementById("unit"));
function MoveUnit(unit){
    const rect = board.getBoundingClientRect();
    const checkX = unit.clientWidth/2 - rect.left;
    const checkY = unit.clientHeight/2 - rect.top;
    var initialX;
    var initialY;
    var tile;
    unit.onmousedown = mouseDown;
    function mouseDown(e){
        e = e || window.event;
        e.preventDefault();
        initialX = e.clientX;
        initialY = e.clientY;
        document.onmouseup = mouseUp;
        document.onmousemove = moveMouse;
    }
    function moveMouse(e){
        e = e || window.event;
        e.preventDefault();
        unit.style.top = (unit.offsetTop   e.clientY - initialY)   "px";
        unit.style.left = (unit.offsetLeft   e.clientX - initialX)   "px";

        boxes.forEach(box => {
            if (ctxB.isPointInPath(box, unit.offsetLeft   checkX, unit.offsetTop   checkY)) {
                ctxB.lineWidth = 2;
                ctxB.fillStyle = 'green';
                ctxB.fill(box);
                ctxB.stroke(box);
                tile=box;
            }else{
                ctxB.lineWidth = 1;
                ctxB.strokeStyle = "black";
                ctxB.fillStyle = 'white';
                ctxB.fill(box);
                ctxB.stroke(box);
            }
        });
        // saving new mousepos after moving the unit
        initialX = e.clientX;
        initialY = e.clientY;
    }
    function mouseUp(){
        document.onmousemove = false;
        
    }
}
function loop(timestamp){    
    // draw once
    if(Grid==false) drawGrid();
    requestAnimationFrame(loop);
}
loop();
#board{
    background-color: #999;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}
#unit{
    background-color: rgb(134, 162, 224);
    position: absolute;
    cursor: pointer;
    z-index: 1;
    width: 30px;
    height: 30px;
}
<!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">
    <link rel="stylesheet" href="testing.css"/>
    <title>Gridding</title>
</head>
<body>
    <div id="unit"></div>
    <canvas id="board"></canvas></div>
    <script src="testing.js"></script>
</body>
</html>

CodePudding user response:

In those Path2D instances we can add some useful data that can do what you need:

let box = new Path2D();
box.rect(...
box.data = { row, column }

then on the function mouseUp we use that to 'snap' into the middle

unit.style.top = ((box.data.column   0.5) * boxsize)   "px";
unit.style.left = ((box.data.row   0.5) * boxsize)   "px";

I reduced a lot of your code on my example below, I'm focusing just on the question, you should do the same when asking questions, that helps others get to the point faster and gives you a better chance of a quick answer.

const board = document.getElementById("board");
const ctxB = board.getContext("2d");
const unit = document.getElementById("unit");

const boxsize = 32;
board.width = board.height = boxsize * 4;

let boxes = [];
for (let r = 0; r < 4; r  ) {
  for (let c = 0; c < 4; c  ) {
    let box = new Path2D();
    box.rect(r * boxsize, c * boxsize, boxsize -0.5, boxsize -0.5);
    box.data = { r, c }
    boxes.push(box);
  }
}

var position = { x: -1, y: -1 }
function mouseDown(e) {
  document.onmouseup = mouseUp;
  document.onmousemove = moveMouse;
  position = { x: e.clientX, y: e.clientY}
}

function mouseUp() {
  document.onmousemove = false;
  boxes.forEach(box => {
    if (ctxB.isPointInPath(box, position.x, position.y)) {
      unit.style.top = ((box.data.c   0.5) * boxsize)   "px";
      unit.style.left = ((box.data.r   0.5) * boxsize)   "px";
    }
  });
}

function moveMouse(e) {
  unit.style.top = (unit.offsetTop   e.clientY - position.y)   "px";
  unit.style.left = (unit.offsetLeft   e.clientX - position.x)   "px";
  position = { x: e.clientX, y: e.clientY}
}

function loop(timestamp) {
  ctxB.clearRect(0, 0, board.width, board.height)
  boxes.forEach(box => {
    ctxB.fillStyle = ctxB.isPointInPath(box, position.x, position.y)? 'green' : 'white'
    ctxB.fill(box);
    ctxB.stroke(box);
  });
  requestAnimationFrame(loop);
}
loop();
unit.onmousedown = mouseDown;
#unit {
  background-color: blue;
  position: absolute;
  width: 20px;
  height: 20px;
}
<canvas id="board"></canvas>
<br>
<div id="unit"></div>

In my code I'm separating the mouse events from the drawing,

  • we draw only on the loop.
  • the events change state variables.

Also noticed that in some cases two boxes got highlighted at the same time changing the size of the rect by a tiny bit fixed that boxsize -0.5

  • Related