使每个画布线可拖动和可拖放

时间:2017-07-05 10:17:51

标签: javascript jquery html canvas

我可以在画布上画一些线条。 我想让每一行都可以拖动和放置。

但是,它是通过在数组中存储不同行的位置来实现的,我怎样才能使它们中的每一个都像一个可拖动和可拖放的实例?或者我弄错了?

您可以在这里查看代码

var storedLines = []

http://jsfiddle.net/m1erickson/NnZ7a/

非常感谢!

1 个答案:

答案 0 :(得分:4)

Canvas draggables。

要制作可拖动的行,您需要将一组端点和一行数组作为端点的索引。

然后,当用户点击并拖动终点或线附近时,您只需按鼠标移动的数量更新终点。

一个点,一行和一些列表

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

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)并突出显示将要生效的对象,以便用户知道将要发生的操作以及他们单击和拖动时的操作。

此外,您应该将鼠标事件设置为文档而不是画布,因为这将捕获鼠标拖动,允许用户在您仍然获取鼠标并移动事件时拖动到画布外。

创建并拖动点和线。

&#13;
&#13;
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);
&#13;
canvas { 
  position : absolute; 
  top : 0px; 
  left : 0px; 
}
&#13;
<canvas id="canvas"></canvas>
&#13;
&#13;
&#13;