Редактирование выбора точек пути или линии - PullRequest
0 голосов
/ 22 ноября 2018

Я пытаюсь найти способ перетаскивания точки или линии многоугольника с помощью javascript на холсте html5, выбирая точки пути щелчком мыши по нему

Вид, подобный изображенному ниже 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 голосов
/ 23 ноября 2018

Данный ответ является примером плохой практики в отношении работы с мышью и рендеринга контента.

Рендеринг от мышиных событий потребует энергии и заставит рендеры холста, когда они не нужныМышь может срабатывать со скоростью до 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>
0 голосов
/ 22 ноября 2018

Я сделал несколько изменений в вашем коде.Так как мне нужно определить положение мыши, я добавил для этого функцию.

Логика такова:

  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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...