我正在使用此代码在画布上绘制箭头:
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();
}
(代码见于此答案:partly answered)。
我对此代码的问题在于,由于ctx.lineWidth,arrowHead指向指定的坐标(tox,toy)。在所提供的示例中,由于lineWidth = 10px,箭头指向画布y位置110。我希望它完全指向tox,玩具坐标,与箭头角度无关。
我的解决方法是将箭头的初始长度减少lineWidth的数量,但我在考虑它的箭头角度时失败了。
答案 0 :(得分:4)
只需要一点点触发即可。 Bellow是一个解决3种类型的线连接的片段。斜角,斜角和圆角。
函数drawArrow
从x,y,xx,yy绘制一条线,options
设置箭头的各种尺寸。
该函数读取ctx.lineWidth
和ctx.lineJoin
值,以确定如何移动终点以确保它不会通过该点。
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);
})();