我能够在 Canvas 上画一些线。
我想使每行可拖动和可拖放。

但是,它是通过将数组中不同行的位置存储来实现的,如何使它们像可拖放的实例一样呢?还是我弄错了?

您可以在此处查看代码
var storedLines = []
http://jsfiddle.net/m1erickson/NnZ7a/

非常感谢!

最佳答案

Canvas 可拖动物品。

要制作可拖动的线,您需要保留一个端点数组和一个线数组作为端点的索引。

然后,当用户单击并拖动到端点或直线附近时,您只需按鼠标移动的数量来更新端点即可。

点,线和一些列表

首先创建一个简单的点结构

const Point2 = (x,y) => ({x,y});  // creates a point

然后是一个列表,该列表将保存点,添加点以及您可能需要的其他任何内容。
const list = {
    items : null,
    add(item) { this.items.push(item); return item },
    eachItem(callback) {
        var i;
        while(i < this.items.length){
             callback(this.items[i],i);
        }
    }
}

然后从列表结构创建一个点列表
function createList(extend){
    return Object.assign({},list,{items : []},extend);
}

const points = createList();

这条线是一组点索引
const Line = (p1,p2) => ({p1,p2});
const lines = createList();

寻找最接近的

要从鼠标坐标中选择一个点,您需要找到最接近鼠标的点。
// this will extend the points list
function getClosestPoint(from ,minDist) {
    var closestPoint;
    this.eachItem(point => {
        const dist = Math.hypot(from.x - point.x, from.y - point.y);
        if(dist < minDist){
            closestPoint = point;
            minDist = dist;
        }
    });
    return closestPoint;
}

该行相同,但不是那么简单。您需要一个函数来查找点与线段的距离。
function distanceLineFromPoint(line,point){
    const lx = points.items[line.p1].x;
    const ly = points.items[line.p1].y;
    const v1x = points.items[line.p2].x - lx;
    const v1y = points.items[line.p2].y - ly;
    const v2x = point.x - lx;
    const v2y = point.y - ly;
    // get unit dist of closest point
    const u = (v2x * v1x + v2y * v1y)/(v1y * v1y + v1x * v1x);
    if(u >= 0 && u <= 1){  // is the point on the line
        return Math.hypot(lx + v1x * u - point.x, ly + v1y * u - point.y);
    } else if ( u < 0 ) {  // point is before start
        return Math.hypot(lx - point.x, ly - point.y);
    }
    // point is after end of line
    return Math.hypot(points.items[line.p2].x - point.x, points.items[line.p2].y - point.y);
}

// this will extend the lines list
function getClosestline(from ,minDist) {
    var closestLine;
    this.eachItem(line => {
        const dist = distanceLineFromPoint(line,from);
        if(dist < minDist){
            closestLine = line;
            minDist = dist;
        }
    });
    return closestLine;
}

使用这些功能,我们应该扩展了列表对象,因此将使用扩展名重新创建它们。
 const points = createList({getClosest : getClosestPoint});
 const lines = createList({getClosest : getClosestline});

然后其余的实现鼠标界面和渲染功能。您添加可拖动的点和连接它们的线。如果单击直线附近或指向点,则将其拖动。片段显示了其余部分。

用户反馈很重要

显示正确的用户反馈也很重要。您需要设置光标,工具提示(通过canvas.style.cursor和canvas.title)并突出显示将要实现的对象,以便用户知道将要执行的操作以及单击和拖动时将执行的操作。

另外,您应该将鼠标事件设置为文档而不是 Canvas ,因为这将捕获鼠标拖动,从而允许用户在仍然获取mouseup和move事件的同时将其拖动到 Canvas 之外。

创建并拖动点和线。

const ctx = canvas.getContext("2d");
const Point2 = (x,y) => ({x,y});  // creates a point
const Line = (p1,p2) => ({p1,p2});
const setStyle = (style) => eachOf(Object.keys(style), key => { ctx[key] = style[key] } );
const eachOf = (array, callback) => {var i = 0; while (i < array.length && callback(array[i],i ++) !== true ); };


const list = {
    items : null,
    add(item) { this.items.push(item); return item },
    eachItem(callback) {
        var i = 0;
        while(i < this.items.length){
             callback(this.items[i],i++);
        }
    }
}
function createList(extend){
    return Object.assign({},list,{items : []},extend);
}
// this will extend the points list
function getClosestPoint(from ,minDist) {
    var closestPoint;
    this.eachItem(point => {
        const dist = Math.hypot(from.x - point.x, from.y - point.y);
        if(dist < minDist){
            closestPoint = point;
            minDist = dist;
        }
    });
    return closestPoint;
}
function distanceLineFromPoint(line,point){
    const lx = points.items[line.p1].x;
    const ly = points.items[line.p1].y;
    const v1x = points.items[line.p2].x - lx;
    const v1y = points.items[line.p2].y - ly;
    const v2x = point.x - lx;
    const v2y = point.y - ly;
    // get unit dist of closest point
    const u = (v2x * v1x + v2y * v1y)/(v1y * v1y + v1x * v1x);
    if(u >= 0 && u <= 1){  // is the point on the line
        return Math.hypot(lx + v1x * u - point.x, ly + v1y * u - point.y);
    } else if ( u < 0 ) {  // point is before start
        return Math.hypot(lx - point.x, ly - point.y);
    }
    // point is after end of line
    return Math.hypot(points.items[line.p2].x - point.x, points.items[line.p2].y - point.y);
}
// this will extend the lines list
function getClosestline(from ,minDist) {
    var closestLine;
    this.eachItem(line => {
        const dist = distanceLineFromPoint(line,from);
        if(dist < minDist){
            closestLine = line;
            minDist = dist;
        }
    });
    return closestLine;
}
function drawPoint(point){
    ctx.moveTo(point.x,point.y);
    ctx.rect(point.x - 2,point.y - 2, 4,4);
}
function drawLine(line){
    ctx.moveTo(points.items[line.p1].x,points.items[line.p1].y);
    ctx.lineTo(points.items[line.p2].x,points.items[line.p2].y);
}
function drawLines(){ this.eachItem(line => drawLine(line)) }
function drawPoints(){this.eachItem(point => drawPoint(point)) }

const points = createList({
  getClosest : getClosestPoint,
  draw : drawPoints,
});
const lines = createList({
  getClosest : getClosestline,
  draw : drawLines,
});
const mouse  = {x : 0, y : 0, button : false, drag : false, dragStart : false, dragEnd : false, dragStartX : 0, dragStartY : 0}
function mouseEvents(e){
	mouse.x = e.pageX;
	mouse.y = e.pageY;
	const lb = mouse.button;
	mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
	if(lb !== mouse.button){
		if(mouse.button){
			mouse.drag = true;
			mouse.dragStart = true;
			mouse.dragStartX = mouse.x;
			mouse.dragStartY = mouse.y;
		}else{
			mouse.drag = false;
			mouse.dragEnd = true;
		}
	}
}
["down","up","move"].forEach(name => document.addEventListener("mouse" + name, mouseEvents));
// short cut vars
var w = canvas.width;
var h = canvas.height;
var cw = w / 2;  // center
var ch = h / 2;
var globalTime;
var closestLine;
var closestPoint;
var pointDrag; // true is dragging a point else dragging a line
var dragOffsetX;
var dragOffsetY;
var cursor;
var toolTip;
var helpCount = 0;
const minDist = 20;
const lineStyle = {
  lineWidth : 2,
  strokeStyle : "green",
}
const pointStyle = {
  lineWidth : 1,
  strokeStyle : "blue",
}
const highlightStyle = {
  lineWidth : 3,
  strokeStyle : "red",
}
const font = {
  font : "18px arial",
  fillStyle : "black",
  textAlign : "center",
}


// main update function
function update(timer){
    cursor = "crosshair";
    toolTip = helpCount < 2 ? "Click drag to create a line" : "";
    globalTime = timer;
    ctx.setTransform(1,0,0,1,0,0); // reset transform
    ctx.globalAlpha = 1;           // reset alpha
	if(w !== innerWidth || h !== innerHeight){
		cw = (w = canvas.width = innerWidth) / 2;
		ch = (h = canvas.height = innerHeight) / 2;
	}else{
		ctx.clearRect(0,0,w,h);
	}
  if(mouse.drag=== false){
    closestLine = undefined;
    closestPoint = points.getClosest(mouse,minDist);
    if(closestPoint === undefined){
       closestLine = lines.getClosest(mouse,minDist);
    }
    if(closestPoint || closestLine){
       toolTip = "Click drag to move " + (closestPoint ? "point" : "line");
       cursor = "move";
    }
  }
  if(mouse.dragStart){
    if(closestPoint){
      dragOffsetX = closestPoint.x - mouse.x;
      dragOffsetY =  closestPoint.y - mouse.y;
      pointDrag = true;

    }else if( closestLine){
      dragOffsetX = points.items[closestLine.p1].x - mouse.x;
      dragOffsetY = points.items[closestLine.p1].y - mouse.y;
      pointDrag = false;

    } else {
      points.add(Point2(mouse.x,mouse.y));
      closestPoint = points.add(Point2(mouse.x,mouse.y));
      closestLine = lines.add(Line(points.items.length-2,points.items.length-1));
      dragOffsetX = 0;
      dragOffsetY = 0;
      pointDrag = true;
      helpCount += 1;

    }
    mouse.dragStart = false;

  }else if(mouse.drag){
      cursor = 'none';
      if(pointDrag){
        closestPoint.x = mouse.x + dragOffsetX;
        closestPoint.y = mouse.y + dragOffsetY;
      }else{
        const dx = mouse.x- mouse.dragStartX;
        const dy = mouse.y -mouse.dragStartY;
        mouse.dragStartX = mouse.x;
        mouse.dragStartY = mouse.y;
        points.items[closestLine.p1].x +=  dx;
        points.items[closestLine.p1].y +=  dy;
        points.items[closestLine.p2].x +=  dx;
        points.items[closestLine.p2].y +=  dy;
      }
  }else{


  }
  // draw all points and lines
  setStyle(lineStyle);
  ctx.beginPath();
  lines.draw();
  ctx.stroke();
  setStyle(pointStyle);
  ctx.beginPath();
  points.draw();
  ctx.stroke();


  // draw highlighted point or line
  setStyle(highlightStyle);
  ctx.beginPath();
  if(closestLine){ drawLine(closestLine) }
  if(closestPoint){ drawPoint(closestPoint) }

  ctx.stroke();


  if(helpCount < 2){
     setStyle(font);
     ctx.fillText(toolTip,cw,30);
  }


  canvas.style.cursor = cursor;
  if(helpCount < 5){
      canvas.title = toolTip;
  }else{
      canvas.title = "";
  }
  requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas {
  position : absolute;
  top : 0px;
  left : 0px;
}
<canvas id="canvas"></canvas>

关于javascript - 使每条 Canvas 线可拖放,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44923395/

10-09 13:42