Home > Mobile >  HTML Canvas drag and drop snap functionality
HTML Canvas drag and drop snap functionality

Time:10-26

I made this simple example of a drag-and-drop circle inside an HTML canvas. Here's the code below:

var c = document.getElementById('myCanvas');
var ctx = c.getContext('2d');
width = c.width = window.innerWidth * 0.9;
height = c.height = window.innerHeight * 0.9;

var handle = {
    x: width / 2,
    y: height / 2,
    radius: 30,
};

function draw() {
    ctx.clearRect(0, 0, width, height);
    ctx.beginPath();
    ctx.arc(handle.x, handle.y, handle.radius, 0, Math.PI * 2, false);
    ctx.fill();
    ctx.stroke();
    drawLines();
}

function drawLines() {
    ctx.beginPath();
    ctx.moveTo(0, height / 2);
    ctx.lineTo(width, height / 2);
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(width / 6, 0);
    ctx.lineTo(width / 6, height);
    ctx.stroke();
}

function circlePointCollision(x, y, circle) {
    return distanceXY(x, y, circle.x, circle.y) < circle.radius;
}

function distanceXY(x0, y0, x1, y1) {
    var dx = x1 - x0,
        dy = y1 - y0;
    return Math.sqrt(dx * dx   dy * dy);
}

document.addEventListener('mousedown', function (e) {
    if (circlePointCollision(e.x , e.y , handle)) {
        document.addEventListener('mousemove', onm ouseMove);
        document.addEventListener('mouseup', onm ouseUp);
    }
});

function onm ouseMove(e) {
    handle.x = e.pageX;
    handle.y = e.pageY;
    draw();
}

function onm ouseUp() {
    document.removeEventListener('mousemove', onm ouseMove);
    document.removeEventListener('mouseup', onm ouseUp);
}
draw();
drawLines();
<!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>Document</title>
  </head>
  <body>
    <canvas id="myCanvas" style="border: 1px solid black"></canvas>
    <script src="./script.js"></script>
  </body>
</html>

I created this with a help of some resources from this video and these are the materials they used. I'm trying to write a proof of concept for snap functionality. Basically, I want to make that circle "snap" in the middle of the crossing two lines when it's being moved across (or very close to). When it's snapped in the intersection, it should be a little bit harder to move it from there. Due to being relatively new to HTML canvas, I'm not exactly sure what the best approach is.

Can anyone help me to expand my snippet and make it do what I need?

CodePudding user response:

Before assigning handle.x and handle.y in onMouseMove simply compare e.pageX and e.pageY to the intersection point and if they are close enough set the the handle coordinates to the intersection instead of e.pageX and e.pageY.

I've created local variables here as an example, but of course you would want to either have a snap function that maintained the threshold and a list of intersections (or a grid declaration of some sort) to check against or declare threshold and intersection globally and check against them.

function onm ouseMove(e) {
  const threshold = handle.radius * 0.8;
  const intersection = { x: width / 6, y: height / 2 }

  handle.x = distanceXY(e.pageX, e.pageY, intersection.x, intersection.y) < threshold ? intersection.x : e.pageX;
  handle.y = distanceXY(e.pageX, e.pageY, intersection.x, intersection.y) < threshold ? intersection.y : e.pageY;
  draw();
}

var c = document.getElementById('myCanvas');
var ctx = c.getContext('2d');
width = c.width = window.innerWidth * 0.9;
height = c.height = window.innerHeight * 0.9;


var handle = {
  x: width / 2,
  y: height / 2,
  radius: 30,
};

function draw() {
  ctx.clearRect(0, 0, width, height);
  ctx.beginPath();
  ctx.arc(handle.x, handle.y, handle.radius, 0, Math.PI * 2, false);
  ctx.fill();
  ctx.stroke();
  drawLines();
}

function drawLines() {
  ctx.beginPath();
  ctx.moveTo(0, height / 2);
  ctx.lineTo(width, height / 2);
  ctx.stroke();

  ctx.beginPath();
  ctx.moveTo(width / 6, 0);
  ctx.lineTo(width / 6, height);
  ctx.stroke();
}

function circlePointCollision(x, y, circle) {
  return distanceXY(x, y, circle.x, circle.y) < circle.radius;
}

function distanceXY(x0, y0, x1, y1) {
  var dx = x1 - x0,
    dy = y1 - y0;
  return Math.sqrt(dx * dx   dy * dy);
}

document.addEventListener('mousedown', function (e) {
  if (circlePointCollision(e.x, e.y, handle)) {
    document.addEventListener('mousemove', onm ouseMove);
    document.addEventListener('mouseup', onm ouseUp);
  }
});

function onm ouseMove(e) {
  const threshold = handle.radius * 0.8;
  const intersection = { x: width / 6, y: height / 2 }

  handle.x = distanceXY(e.pageX, e.pageY, intersection.x, intersection.y) < threshold ? intersection.x : e.pageX;
  handle.y = distanceXY(e.pageX, e.pageY, intersection.x, intersection.y) < threshold ? intersection.y : e.pageY;
  draw();
}

function onm ouseUp() {
  document.removeEventListener('mousemove', onm ouseMove);
  document.removeEventListener('mouseup', onm ouseUp);
}
draw();
drawLines();
<!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>Document</title>
  </head>
  <body>
    <canvas id="myCanvas" style="border: 1px solid black"></canvas>
    <script src="./script.js"></script>
  </body>
</html>

  • Related