带有修改点的箭头

问题描述 投票:0回答:1

嗨,我正在尝试再现draw.io效果。绘制箭头时,它会在箭头中间显示一个蓝点,允许您在两条线之间创建角度,并且显示两个蓝点,使您可以对两条新线进行相同操作。我在下面添加了图片。会更容易理解。我想知道如何动态编码这些允许“中断”线的蓝点enter image description hereenter image description here

var ctx = tempcanvas.getContext('2d'),
    mainctx = canvas.getContext('2d'),
    w = canvas.width,
    h = canvas.height,
    x1,
    y1,
    isDown = false;

ctx.translate(0.5, 0.5);

tempcanvas.onmousedown = function(e) {
    var rect = canvas.getBoundingClientRect();
    x1 = e.clientX - rect.left;
    y1 = e.clientY - rect.top;
    isDown = true;
}
tempcanvas.onmouseup = function() {
    isDown = false;
    mainctx.drawImage(tempcanvas, 0, 0);
    ctx.clearRect(0, 0, w, h);
}
tempcanvas.onmousemove = function(e) {

    if (!isDown) return;
    
    var rect = canvas.getBoundingClientRect(),
        x2 = e.clientX - rect.left,
        y2 = e.clientY - rect.top;
    var p0={x1,y1};
    var p1={x2,y2};


    ctx.clearRect(0, 0, w, h);
    drawLineWithArrowhead(p0,p1,25);

}




function drawLineWithArrowhead(p0,p1,headLength){

  
  var PI=Math.PI;
  var degreesInRadians225=225*PI/180;
  var degreesInRadians135=135*PI/180;

  
  var dx=p1.x2-p0.x1;
  var dy=p1.y2-p0.y1;
  var angle=Math.atan2(dy,dx);

  // calc arrowhead points
  var x225=p1.x2+headLength*Math.cos(angle+degreesInRadians225);
  var y225=p1.y2+headLength*Math.sin(angle+degreesInRadians225);
  var x135=p1.x2+headLength*Math.cos(angle+degreesInRadians135);
  var y135=p1.y2+headLength*Math.sin(angle+degreesInRadians135);

  
  ctx.beginPath();
  // draw the line from p0 to p1
  ctx.moveTo(p0.x1,p0.y1);
  ctx.lineTo(p1.x2,p1.y2);
  // draw partial arrowhead at 225 degrees
  ctx.moveTo(p1.x2,p1.y2);
  ctx.lineTo(x225,y225);
  // draw partial arrowhead at 135 degrees
  ctx.moveTo(p1.x1,p1.y1);
  ctx.lineTo(x135,y135);
  // stroke the line and arrowhead
  ctx.stroke();
}
canvas {position:absolute;left:0;top:0}
#canvas {background:#eef}
<canvas id="canvas" width=400 height=400></canvas>
<canvas id="tempcanvas" width=400 height=400></canvas>
javascript html5-canvas
1个回答
0
投票

示例片段

很抱歉(周末和所有时间)编写详细的说明,不要浪费代码,因此希望对您有所帮助。

const ctx = canvas.getContext("2d");
ctx.bounds = canvas.getBoundingClientRect();
const P2 = (x = 0, y = 0) => ({x, y});
const points = [];
const lineStyle = "#000";
const nearLineStyle = "#0AF";
const lineWidth = 2;
const nearLineWidth = 3;
const pointStyle = "#000";
const nearPointStyle = "#0AF";
const pointLineWidth = 1;
const nearPointLineWidth = 2;
const arrowSize = 18;
const pointSize = 5;
const nearPointSize = 15;
const checkerSize = 256;  // power of two
const checkerCol1 = "#CCC";
const checkerCol2 = "#EEE";
const MIN_SELECT_DIST = 20; // in pixels;
var w = canvas.width, h = canvas.height;
var cw = w / 2, ch = h / 2;
var cursor = "default";
var toolTip = "";
const mouse = { x: 0, y: 0, button: 0 };
const drag = {dragging: false};
requestAnimationFrame(update);

function mouseEvents(e) {
    mouse.x = e.pageX - ctx.bounds.left - scrollX;
    mouse.y = e.pageY - ctx.bounds.top - scrollY;
    if (e.type === "mousedown") { mouse.button |= 1 << (e.which - 1) }
    else if (e.type === "mouseup") { mouse.button &= ~(1 << (e.which - 1)) }
}
["down", "up", "move"].forEach(name => document.addEventListener("mouse" + name, mouseEvents));
const checkerboard = (()=> {
    const s = checkerSize, s2 = s / 2;
    const c = document.createElement("canvas");
    c.height = c.width = checkerSize;
    const ctx = c.getContext("2d", {alpha: false});
    ctx.fillStyle = checkerCol1;
    ctx.fillRect(0,0,s, s);
    ctx.fillStyle = checkerCol2;
    ctx.fillRect(0,0,s2,s2);
    ctx.fillRect(s2,s2,s2,s2);
    ctx.globalAlpha = 0.25;
    var ss = s2;
    while(ss > 8) {
        ctx.fillStyle = ctx.createPattern(c, "repeat");  
        ctx.setTransform(1/8,0,0,1/8,0,0);
        ctx.fillRect(0,0,s * 8,s * 8);
        ss /= 2;
    }
    return ctx.createPattern(c, "repeat");   
})();

function nearestPointLine(points, point, minDist){   // fills nearest object with nearest point and line to point if within minDist.
    var i = 0, p1, dist;
    nearest.reset(minDist);
    const v1 = P2();
    const v2 = P2();
    const v3 = P2();
    for (const p of points) {
        v2.x = point.x - p.x;
        v2.y = point.y - p.y;
        dist = (v2.x * v2.x + v2.y * v2.y) ** 0.5;
        if(dist < nearest.point.dist) {
            nearest.point.dist = dist;
            nearest.point.p = p;
            nearest.point.idx = i;
        }           
        if (p1) {
            v1.x = p1.x - p.x;
            v1.y = p1.y - p.y;
            v2.x = point.x - p.x;
            v2.y = point.y - p.y;
            const u = (v2.x * v1.x + v2.y * v1.y) / (v1.y * v1.y + v1.x * v1.x);
           
            if (u >= 0 && u <= 1) { // is closest poin on line segment
                v3.x = p.x + v1.x * u;
                v3.y = p.y + v1.y * u;
                //ctx.fillRect(v3.x, v3.y, 5, 5)
                dist = ((v3.y - point.y) ** 2 + (v3.x - point.x) ** 2) ** 0.5;
                if(dist < nearest.line.dist) {
                    nearest.line.dist = dist;
                    nearest.line.p1 = p1;
                    nearest.line.p2 = p;
                    nearest.line.idx = i;
                    nearest.line.onLine.x = v3.x;
                    nearest.line.onLine.y = v3.y;
                }
            }
        }
        p1 = p;
        i ++;
    }
    if (nearest.point.idx > -1 && nearest.point.dist / 2 <= nearest.line.dist) {        
        nearest.active = nearest.point;
        nearest.near = true;
    } else if (nearest.line.idx > -1) {
        nearest.active = nearest.line;
        nearest.near = true;
    }
}
function drawLine(p1, p2) {
    ctx.moveTo(p1.x, p1.y);
    ctx.lineTo(p2.x, p2.y);
}
function drawLineArrow(p1, p2) {
    var nx = p1.x - p2.x;
    var ny = p1.y - p2.y;
    const d =( nx * nx + ny * ny) ** 0.5;
    if(d > arrowSize) {
        nx /= d;
        ny /= d;
        ctx.setTransform(-nx, -ny, ny, -nx, p2.x, p2.y);
        ctx.beginPath()
        ctx.fillStyle = ctx.strokeStyle;
        ctx.moveTo(0, 0);
        ctx.lineTo(-arrowSize, arrowSize / 2);
        ctx.lineTo(-arrowSize, -arrowSize / 2);
        ctx.fill();
        ctx.setTransform(1,0,0,1,0,0);
    }
}
function drawPoint(p, size = pointSize) {
    ctx.rect(p.x - size / 2, p.y - size / 2, size, size);
}
function drawLines(points) {
    var p1;
    ctx.strokeStyle = lineStyle;
    ctx.lineWidth = lineWidth;
    ctx.beginPath()
    for(const p of points) {
        if (p1) { drawLine(p1 ,p) }
        p1 = p;
    }
    ctx.stroke();
    if(points.length > 1) {
        drawLineArrow(points[points.length - 2], p1);
    }
}
function drawPoints(points) {
    ctx.strokeStyle = pointStyle;
    ctx.lineWidth = pointLineWidth;
    ctx.beginPath()
    for(const p of points) { drawPoint(p) }
    ctx.stroke();
}
function sizeCanvas() { 
    if (w !== innerWidth || h !== innerHeight) {
        cw = (w = canvas.width = innerWidth) / 2;
        ch = (h = canvas.height = innerHeight) / 2;
        ctx.bounds = canvas.getBoundingClientRect();
    }
}
const nearest = {
    point: { isPoint: true },
    line: { onLine: P2() },
    reset(minDist) {
        nearest.point.dist = minDist;
        nearest.point.idx = -1;
        nearest.line.dist = minDist;
        nearest.line.idx = -1;
        nearest.active = null;
        nearest.near = false;
    },
    draw() {
        const a = nearest.active;
        if (a) {
            if (a.isPoint) {
                ctx.strokeStyle = nearPointStyle;
                ctx.lineWidth = nearPointLineWidth;
                ctx.beginPath()
                drawPoint(a.p, nearPointSize);
                ctx.stroke();           
            } else {
                ctx.strokeStyle = nearLineStyle;
                ctx.lineWidth = nearLineWidth;
                ctx.beginPath()
                drawLine(a.p1, a.p2);
                ctx.stroke();       
                ctx.strokeStyle = nearPointStyle;
                ctx.lineWidth = nearPointLineWidth;
                ctx.beginPath()
                drawPoint(a.onLine, nearPointSize);
                ctx.stroke();   
            }
        }
    }           
}
function update() {
    cursor = "crosshair";
    toolTip = "";
    ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
    ctx.globalAlpha = 1; // reset alpha
    sizeCanvas();
    ctx.fillStyle = checkerboard;
    ctx.fillRect(0, 0, w, h);
    if (!drag.dragging) { 
        nearestPointLine(points, mouse, MIN_SELECT_DIST);
        if (nearest.near && nearest.active.isPoint) { cursor = "move"; toolTip = "Drag to move point"}
        else if (nearest.near) { cursor = "crosshair"; toolTip = "Click/drag to cut and drag new point"  }
        else { 
            if (points.length < 2) {
                cursor = "crosshair"; 
                toolTip ="Click to add point";
             } else {
                cursor = "default"; 
                toolTip = "";
             }
         }
    }
    drawLines(points);
    drawPoints(points);
    nearest.draw();
    if((mouse.button & 1) === 1) {
        if (!drag.dragging) {
            if(points.length < 2 && !nearest.near) {
                points.push(P2(mouse.x, mouse.y));
                mouse.button = 0;
            } else if (nearest.near) {
                if (nearest.active.isPoint) {
                    drag.point = nearest.active.p;
                } else {
                    drag.point = P2(nearest.active.onLine.x, nearest.active.onLine.y);
                    points.splice(nearest.active.idx, 0, drag.point);
                    nearestPointLine(points, drag.point, 20);
                }
                drag.offX = drag.point.x - mouse.x;
                drag.offY = drag.point.y - mouse.y;
                drag.dragging = true;
            }
        }
        if(drag.dragging) {
            drag.point.x = drag.offX + mouse.x;
            drag.point.y = drag.offY + mouse.y;
            drag.point.x = drag.point.x < 1 ? 1 : drag.point.x > w - 2 ? w - 2 : drag.point.x;
            drag.point.y = drag.point.y < 1 ? 1 : drag.point.y > h - 2 ? h - 2 : drag.point.y;
            cursor = "none";
        }
    } else if((mouse.button & 1) === 0) {
        drag.dragging = false;
        drag.point = null;
    }
    canvas.title = toolTip;
    canvas.style.cursor = cursor;
    requestAnimationFrame(update);
}
canvas {
  position: absolute;
  top: 0px;
  left: 0px;
}
<canvas id="canvas"></canvas>
© www.soinside.com 2019 - 2024. All rights reserved.