Javascript画布箭头 - 箭头没有指向行尾 [英] Javascript canvas arrow - arrowhead not pointing at line end

查看:92
本文介绍了Javascript画布箭头 - 箭头没有指向行尾的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用此代码在画布上绘制箭头:

I am using this code to draw an arrow on a canvas:

var arrowCanvas = document.getElementById("arrowCanvas");
var ctx = arrowCanvas.getContext("2d");
drawArrow(ctx, 30, 10, 30, 100);

function drawArrow(ctx, fromx, fromy, tox, toy){
    //variables to be used when creating the arrow
    var headlen = 10;
    ctx.strokeStyle = "#cc0000";
    ctx.fillStyle = "#cc0000";
    ctx.lineWidth = 10;
    var angle = Math.atan2(toy-fromy,tox-fromx);

    //starting path of the arrow from the start square to the end square and drawing the stroke
    ctx.beginPath();
    ctx.moveTo(fromx, fromy);
    ctx.lineTo(tox, toy);
    ctx.stroke();

    //starting a new path from the head of the arrow to one of the sides of the point
    ctx.beginPath();
    ctx.moveTo(tox, toy);
    ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));

    //path from the side point of the arrow, to the other side point
    ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),toy-headlen*Math.sin(angle+Math.PI/7));

    //path from the side point back to the tip of the arrow, and then again to the opposite side point
    ctx.lineTo(tox, toy);
    ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));

    //draws the paths created above
    ctx.stroke();
    ctx.fill();
}

(代码见于此答案:画布标签上的箭头)。

(Code is found in this answer: Draw arrow on canvas tag).

我的问题代码是因为ctx.lineWidth,arrowHead指向指定的坐标(tox,toy)。在所提供的示例中,由于lineWidth = 10px,箭头指向画布y位置110。我希望它完全指向tox,玩具坐标,与箭头角度无关。

My problem with this code is that because of ctx.lineWidth the arrowHead points over the specified coordinates (tox, toy). In the provided example the arrowhead points onto the canvas y-position 110, due to lineWidth = 10px. I want it to point exactly on the tox, toy-coordinates, independent of the arrows angle.

我的解决方案是减少箭头的初始长度of lineWidth,但我没有考虑它的箭头角度。

My solution would be to reduce the initial length of the arrow by the amount of lineWidth, but I failed at considering the arrow angle for it.

推荐答案

只需要一点点触发即可。 Bellow是一个解决3种类型的线连接的片段。斜角,斜角和圆角。

A little trig is all that is needed. Bellow is a snippet that solves the 3 types of line joins. Bevel, Miter, and round.

函数 drawArrow 从x,y,xx,yy绘制一条线 options 设置箭头的各种尺寸。

The function drawArrow draw a line from x,y,xx,yy with options setting the various dimensions for the arrow head.

该函数读取 ctx。 lineWidth ctx.lineJoin 值可以计算出如何移动终点以确保它不会通过该点。

The function reads the ctx.lineWidth and ctx.lineJoin values to work out how to move the end point to ensure it does not pass the point.


  • 对于圆形连接,移动距离是线宽的一半

  • 对于斜接,距离是一半线宽划分箭头尖角的一半罪

  • 对于斜角,距离是尖端的角度的一半乘以半线宽度

const PI = Math.PI;
const PI2 = PI * 2;
function drawArrow(ctx,x,y,xx,yy,options){
    function getDef(name,def){return options[name] !== undefined ? options[name] : def;}
    var w = getDef("width",5); // get settings
    var hs = getDef("headSize",15); // 
    var hw = getDef("headWidth",15); // 
    var dx = xx-x;
    var dy = yy-y;
    var dir = Math.atan2(dy,dx);
    var dist = Math.sqrt(dx*dx+dy*dy);
    var lineWidth = Number(ctx.lineWidth)
    var endMove = ctx.lineWidth/2; // assume round joins
    if(ctx.lineJoin === "miter"){
        endMove = Math.min(ctx.miterLimit,endMove / (hw / Math.sqrt(hs*hs+hw*hw)));
    }else if(ctx.lineJoin === "bevel"){
        endMove = endMove * Math.cos(Math.asin(hs / Math.sqrt(hs*hs+hw*hw)));
    }
    // move canvas coordinates so that the arrow starts at 0,0, 
    ctx.setTransform(1,0,0,1,x,y); 
    ctx.rotate(dir); // and is aligned to x
    dist -= endMove; // shorten for line width
    ctx.beginPath();
    ctx.moveTo(0,-w);
    ctx.lineTo(dist - hs,-w);
    ctx.lineTo(dist - hs,-hw);
    ctx.lineTo(dist ,0);
    ctx.lineTo(dist - hs,hw);
    ctx.lineTo(dist - hs,w);
    ctx.lineTo(0,w);
    ctx.stroke();
    ctx.fill();
}

var arrows = [
    {width : 5, headWidth : 10, headSize : 20, lineWidth : 5,line : "red", fill : "blue",join : "bevel", limit : 100},
    {width : 10, headWidth : 20, headSize : 20, lineWidth : 5,line : "Orange", fill : "blue",join : "miter", limit : 5},
    {width : 10, headWidth : 20, headSize : 20, lineWidth : 5,line : "Green", fill : "blue",join : "round", limit : 0},
]
var tempArrow = {width : 10, headWidth : 20, headSize : 20};
const numArrows = 3;
const mouseClear = 30;  


// main update function
function display(){
    ctx.setTransform(1,0,0,1,0,0); // reset transform
    ctx.globalAlpha = 1;           // reset alpha
    ctx.clearRect(0,0,w,h);
    ctx.lineWidth = 1;
    ctx.strokeStyle = "black";
    ctx.beginPath();
    ctx.arc(mouse.x,mouse.y,mouseClear,0,PI2);
    ctx.miterLimit = 1000;
    ctx.stroke();
    for(var i = 0; i < numArrows; i ++){
        var x = cw + Math.cos((i/numArrows)*PI2) * cw *1.8;
        var y = ch + Math.sin((i/numArrows)*PI2) * ch *1.8;
        var dir = Math.atan2(y-mouse.y,x-mouse.x);
        var xx = mouse.x + Math.cos(dir) * mouseClear;
        var yy = mouse.y + Math.sin(dir) * mouseClear;
        var scaleLine = (Math.sin(globalTime/1000)+1.1) * 2;
        var style = arrows[i%arrows.length];
        var arrowHead = (Math.sin(globalTime/770)+1.1) * 2;
        var arrowSize = (Math.sin(globalTime/1370)+1.1) * 2;
        ctx.lineWidth = style.lineWidth * scaleLine;
        ctx.strokeStyle = style.line;
        ctx.fillStyle = style.fill;
        ctx.lineJoin = style.join;
        tempArrow.headWidth = style.headSize * arrowHead; 
        tempArrow.headSize = style.headSize * arrowSize;
        
        
        drawArrow(ctx,x,y,xx,yy,tempArrow);
    }

}






//==============================================================================
// From here down part of answer just boiler room stuff
// can be ignored.













/** SimpleFullCanvasMouse.js begin **/

var w, h, cw, ch, canvas, ctx, mouse, globalTime = 0, firstRun = true;



;(function(){
    const RESIZE_DEBOUNCE_TIME = 100;
    var  createCanvas, resizeCanvas, setGlobals, resizeCount = 0;
    createCanvas = function () {
        var c,
        cs;
        cs = (c = document.createElement("canvas")).style;
        cs.position = "absolute";
        cs.top = cs.left = "0px";
        cs.zIndex = 1000;
        document.body.appendChild(c);
        return c;
    }
    resizeCanvas = function () {
        if (canvas === undefined) {
            canvas = createCanvas();
        }
        canvas.width = innerWidth;
        canvas.height = innerHeight;
        ctx = canvas.getContext("2d");
        if (typeof setGlobals === "function") {
            setGlobals();
        }
        if (typeof onResize === "function") {
            if(firstRun){
                onResize();
                firstRun = false;
            }else{
                resizeCount += 1;
                setTimeout(debounceResize, RESIZE_DEBOUNCE_TIME);
            }
        }
    }
    function debounceResize() {
        resizeCount -= 1;
        if (resizeCount <= 0) {
            onResize();
        }
    }
    setGlobals = function () {
        cw = (w = canvas.width) / 2;
        ch = (h = canvas.height) / 2;
    }
    mouse = (function () {
        function preventDefault(e) {
            e.preventDefault();
        }
        var mouse = {
            x : 0,
            y : 0,
            w : 0,
            alt : false,
            shift : false,
            ctrl : false,
            buttonRaw : 0,
            over : false,
            bm : [1, 2, 4, 6, 5, 3],
            active : false,
            bounds : null,
            crashRecover : null,
            mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
        };
        var m = mouse;
        function mouseMove(e) {
            var t = e.type;
            m.bounds = m.element.getBoundingClientRect();
            m.x = e.pageX - m.bounds.left;
            m.y = e.pageY - m.bounds.top;
            m.alt = e.altKey;
            m.shift = e.shiftKey;
            m.ctrl = e.ctrlKey;
            if (t === "mousedown") {
                m.buttonRaw |= m.bm[e.which - 1];
            } else if (t === "mouseup") {
                m.buttonRaw &= m.bm[e.which + 2];
            } else if (t === "mouseout") {
                m.buttonRaw = 0;
                m.over = false;
            } else if (t === "mouseover") {
                m.over = true;
            } else if (t === "mousewheel") {
                m.w = e.wheelDelta;
            } else if (t === "DOMMouseScroll") {
                m.w = -e.detail;
            }
            if (m.callbacks) {
                m.callbacks.forEach(c => c(e));
            }
            e.preventDefault();
        }
        m.addCallback = function (callback) {
            if (typeof callback === "function") {
                if (m.callbacks === undefined) {
                    m.callbacks = [callback];
                } else {
                    m.callbacks.push(callback);
                }
            }
        }
        m.start = function (element) {
            if (m.element !== undefined) {
                m.removeMouse();
            }
            m.element = element === undefined ? document : element;
            m.mouseEvents.forEach(n => {
                m.element.addEventListener(n, mouseMove);
            });
            m.element.addEventListener("contextmenu", preventDefault, false);
            m.active = true;
        }
        m.remove = function () {
            if (m.element !== undefined) {
                m.mouseEvents.forEach(n => {
                    m.element.removeEventListener(n, mouseMove);
                });
                m.element.removeEventListener("contextmenu", preventDefault);
                m.element = m.callbacks = undefined;
                m.active = false;
            }
        }
        return mouse;
    })();



    function update(timer) { // Main update loop
        if(ctx === undefined){
            return;
        }
        globalTime = timer;
        display(); // call demo code
        requestAnimationFrame(update);
    }
    setTimeout(function(){
        resizeCanvas();
        mouse.start(canvas, true);
        window.addEventListener("resize", resizeCanvas);
        requestAnimationFrame(update);
    },0);
})();


 

这篇关于Javascript画布箭头 - 箭头没有指向行尾的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆