2016-12-16 45 views
1

指着我使用这个代码在画布上画一个箭头:的Javascript帆布箭头 - 箭头不在行尾

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(); 
} 

(:Draw arrow on canvas tag代码在这个答案找到)。

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

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

回答

3

小TRIG是所有需要。贝娄是解决3种线路连接的片段。斜角,斜角和圆形。

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

该函数读取ctx.lineWidthctx.lineJoin值来计算如何移动终点以确保其不通过该点。

  • 对于“圆”加入到在移动的距离是半线宽
  • 对于“斜切”的距离为半线宽分割一半的箭头尖角度
  • 的罪对于“斜角”的距离为cos尖尖结束时间的一半的角度的一半线宽

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); 
 
})(); 
 

 

 

+0

给予好评的一个很好的答案...并说明答案[微笑]一个整洁的小动画。 – markE