编辑路径点或线的选择

时间:2018-11-22 09:30:04

标签: javascript html5-canvas

我正在尝试找到一种方法,通过使用鼠标单击选择路径点来在html5画布中使用javascript拖放多边形点或线

如下图所示 enter image description here

通过单击并将其拖动到新位置来移动所选点

var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
  var BB=canvas.getBoundingClientRect();
  offsetX=BB.left;
  offsetY=BB.top;        
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }

context.lineWidth=2;
context.strokeStyle='blue';

var coordinates = [];
var isDone=false;

$('#done').click(function(){
  isDone=true;
});

$("#canvas").mousedown(function(e){handleMouseDown(e);});

function handleMouseDown(e){
  if(isDone || coordinates.length>10){return;}

  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);
  coordinates.push({x:mouseX,y:mouseY});
  drawPolygon();
}

function drawPolygon(){
  context.clearRect(0,0,cw,ch);
  context.beginPath();
  context.moveTo(coordinates[0].x, coordinates[0].y);
  for(index=1; index<coordinates.length;index++) {
    context.lineTo(coordinates[index].x, coordinates[index].y);
  }
  context.closePath();
  context.stroke();
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h4>Click to assign polygon vertices</h4>
<button id=done>Click when done assigning points</button>
<br><canvas id="canvas" width=300 height=300></canvas>

2 个答案:

答案 0 :(得分:2)

我对您的代码进行了一些更改。由于我需要检测鼠标的位置,因此我添加了一个功能来完成此操作。

逻辑如下:

  1. 绘制点。

  2. 完成后,您可以开始拖动点

  3. 如果您在一个点内单击(围绕该点的小圆圈),则会保存该点的索引。

  4. 在鼠标移动时,您移动了单击的点。

  5. 在鼠标上移不再拖动任何点。

我希望这是您所需要的。

var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
var mouse = {};
var draggable = false;

context.lineWidth = 2;
context.strokeStyle = "blue";

var coordinates = [];
var isDone = false;

done.addEventListener("click", function() {
  isDone = true;
});

canvas.addEventListener("mousedown", function(e) {
  handleMouseDown(e);
});

function handleMouseDown(e) {
  mouse = oMousePos(canvas, e);
  //if isDone you can drag
  if (isDone || coordinates.length > 10) {
    for (index = 0; index < coordinates.length; index++) {
    // you draw a small circle no stroke, no fill
      context.beginPath();
      context.arc(
        coordinates[index].x,
        coordinates[index].y,
        5,
        0,
        2 * Math.PI
      );
      // if the mouse is inside the circle
      if (context.isPointInPath(mouse.x, mouse.y)) {
      // you can drag this point
      // I'm using index + 1 because index == 0 is false
        draggable = index + 1; 
      // if I have a point a can break the loop 
        break;
      }
    }
  } else {
    coordinates.push({ x: mouse.x, y: mouse.y });
    drawPolygon();
  }
}

function drawPolygon() {
  context.clearRect(0, 0, cw, ch);
  context.beginPath();
  context.moveTo(coordinates[0].x, coordinates[0].y);
  for (index = 1; index < coordinates.length; index++) {
    context.lineTo(coordinates[index].x, coordinates[index].y);
  }
  context.closePath();
  context.stroke();
  // Additionaly I'm drawing a small circle around every point
  // you can delete this.
  for (index = 0; index < coordinates.length; index++) {
    context.beginPath();
    context.arc(coordinates[index].x, coordinates[index].y, 5, 0, 2 * Math.PI);
    context.stroke();
  }
}

canvas.addEventListener("mousemove", function(e) {
  if (isDone) {
    if (draggable) {
      mouse = oMousePos(canvas, e);
      // draggable - 1 is the index of the point in the coordinates array
      coordinates[draggable - 1].x = mouse.x;
      coordinates[draggable - 1].y = mouse.y;
      drawPolygon();
    }
  }
});

canvas.addEventListener("mouseup", function(e) {
  if (draggable) {
    draggable = false;
  }
});


// a function to detect the mouse position

function oMousePos(canvas, evt) {
  var ClientRect = canvas.getBoundingClientRect();
  return {
    //objeto
    x: Math.round(evt.clientX - ClientRect.left),
    y: Math.round(evt.clientY - ClientRect.top)
  };
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<h4>Click to assign polygon vertices</h4>
<button id=done>Click when done assigning points</button>
<br><canvas id="canvas" width=300 height=300></canvas>

答案 1 :(得分:2)

给出的答案是在处理鼠标和呈现内容方面的不良做法的一个示例。

鼠标事件的渲染会消耗能量,并在不需要时强制画布渲染。鼠标可以以每秒1000次的速度发射,而最大显示速度仅为每秒60次。

使用鼠标移动事件意味着许多渲染的更新将不会被用户看到,并且充其量只是在浪费CPU / GPU周期,最坏的情况是会浪费大量电池。

始终使用requestAnimationFrame呈现任何经常更改的内容。

下面的示例使用requestAnimationFrame从输入事件中取消渲染,以仅在需要时渲染内容。它还通过光标和突出显示的点为用户添加了一些反馈。

var ctx = canvas.getContext("2d");
requestAnimationFrame(update)

mouse = {x : 0, y : 0, button : 0, lx : 0, ly : 0, update : true};
function mouseEvents(e){
	const bounds = canvas.getBoundingClientRect();
	mouse.x = e.pageX - bounds.left - scrollX;
	mouse.y = e.pageY - bounds.top - scrollY;
	mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
  mouse.update = true;
}
["mousedown","mouseup","mousemove"].forEach(name => document.addEventListener(name,mouseEvents));



ctx.lineWidth = 2;
ctx.strokeStyle = "blue";
const point = (x,y) => ({x,y});
const poly = () => ({
    points : [],
    addPoint(p){ this.points.push(point(p.x,p.y)) },
    draw() {
        ctx.lineWidth = 2;
        ctx.strokeStyle = "blue";
        ctx.beginPath();
        for (const p of this.points) { ctx.lineTo(p.x,p.y) }
        ctx.closePath();
        for (const p of this.points) {
            ctx.moveTo(p.x + 4,p.y);
            ctx.arc(p.x,p.y,4,0,Math.PI *2);
        }
        ctx.stroke();
    },
    closest(pos, dist = 8) {
        var i = 0, index = -1;
        dist *= dist;
        for (const p of this.points) {
            var x = pos.x - p.x;
            var y = pos.y - p.y;
            var d2 =  x * x + y * y;
            if (d2 < dist) {
                dist = d2;
                index = i;
            }
            i++;
        }
        if (index > -1) { return this.points[index] }
    }
});
function drawCircle(pos,color="red",size=8){
    ctx.strokeStyle = color;
    ctx.beginPath();
    ctx.arc(pos.x,pos.y,size,0,Math.PI *2);
    ctx.stroke();
}
const polygon = poly();
var activePoint,cursor;
var dragging= false;
function update(){
    if (mouse.update) {
        cursor = "crosshair";
        ctx.clearRect(0,0,canvas.width,canvas.height);
        if (!dragging) {  activePoint = polygon.closest(mouse) }
        if (activePoint === undefined && mouse.button) {
            polygon.addPoint(mouse);
            mouse.button = false;
        } else if(activePoint) {
            if (mouse.button) {
                if(dragging) {
                    activePoint.x += mouse.x - mouse.lx;
                    activePoint.y += mouse.y - mouse.ly;
                } else {  dragging = true }
            } else { dragging = false }
        }
        polygon.draw();
        if (activePoint) { 
            drawCircle(activePoint);
            cursor = "move";
        }

        mouse.lx = mouse.x;
        mouse.ly = mouse.y;
        canvas.style.cursor = cursor;
        mouse.update = false;
    }
    requestAnimationFrame(update)
}
#canvas{
  border:1px 
  solid black;
}
<canvas id="canvas" width=300 height=300></canvas>