HTML画布绘制线垂直于另一条线 [英] HTML canvas drawing line perpendicular on another line

查看:88
本文介绍了HTML画布绘制线垂直于另一条线的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用HTML画布绘制线条,如下图所示,但该线条的两侧都有一条边。





如图所示,两个边缘与主线不是垂直

我尝试了以下解决方案,但没有成功:

*旋转边缘线,但是旋转会将其从原始位置转换为

*找到角度主线,然后相对于该线绘制线,但是这种解决方案不容易实现(很可能我错误地实现了)。



这是我的代码,但是它将始终绘制垂直边缘:

 <!DOCTYPE html> 
< html>
< body>

< canvas id = myCanvas width = 200 height = 200 style = border:1px solid#d3d3d3;>

< script>

var c = document.getElementById( myCanvas);
var ctx = c.getContext( 2d);

var x1 = 100;
var x2 = 150;
var y1 = 50;
var y2 = 120;

ctx.beginPath();
ctx.strokeStyle =紫色; //紫色路径
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);
ctx.stroke(); //绘制

ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x1,y1 + 10);
ctx.stroke();


ctx.restore();
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x1,(y1-10));
ctx.stroke();


ctx.beginPath();
ctx.moveTo(x2,y2);
ctx.lineTo(x2,y2 + 10);
ctx.stroke();


ctx.restore();
ctx.beginPath();
ctx.moveTo(x2,y2);
ctx.lineTo(x2,(y2-10));
ctx.stroke();


< / script>
< / body>
< / html>

有人可以帮助我旋转两条边缘线以使其垂直于主要行
谢谢。

解决方案

将任意2D向量旋转90deg



如果您还记得2D向量的90度旋转规则,则垂直线很容易计算。



向量 {x,y} 可以顺时针 {-y,x} 或逆时针 {y,-x} 。交换x和y并顺时针取反y或逆时针x取反



因此对于线段 x1 y1 x2 y2 转换为向量,将向量归一化并如下旋转90度

  function getPerpOfLine(x1,y1,x2,y2){//这两个点不能相同
var nx = x2-x1; //作为向量
var ny = y2-y1;
const len = Math.sqrt(nx * nx + ny * ny); //行的长度
nx / = len; //使一个单位长
ny / = len; //我们称之为标准化向量
return [-ny,nx]; //返回正常旋转90度的角度
}

然后说要绘制10线段末端的像素线



  const canvas = document.createElement ( canvas); const ctx = canvas.getContext( 2d); document.body.append(canvas); ctx.strokeStyle = black; ctx.lineJoin = ctx.lineCap = round; ctx.lineWidth = 3; //线段const x1 = 40,y1 = 40,x2 = 260,y2 = 110; const endLen = 10; //结束线的长度var px = y1-y2; //作为与线成90度的向量var py = x2-x1; const len = endLen / Math.hypot(px,py); px * = len; //使长10像素py * = len; //绘制起点和终点的线.ctx.beginPath(); ctx.lineTo(x1,y1); //行startctx.lineTo(x2,y2); ctx.moveTo(x1 + px,y1 + py); //起始行linectx.lineTo(x1-px,y1-py); ctx.moveTo(x2 + px,y2 + py); //结束符linectx.lineTo(x2-px,y2-py); ctx.stroke();  



更新



一种简单的解决方案,用于沿线呈现内容,使用相同的90deg规则。 / p>

还有一种使用相同矢量旋转方式进行渲染的方法,但不是通过矢量乘法设置垂直轴,而是将变换的y轴设置为与沿线的x轴。将原点设置为该行的起点,您只需相对于该行进行渲染。



setTransformToLine(x1,y1,x2,y2)



以下函数将画布变换设置为沿线

  //设置沿x1,y1,x2,y2线和原点的2D上下文当前变换到
//行的开始。 y轴从该线顺时针旋转90度。
//返回使用
//节省时间的最常用行长。
函数setTransformToLine(x1,y1,x2,y2){
const vx = x2-x1; //将行作为向量
const vy = y2-y1;
const len = Math.hypot(vx,vy); //对于< = IE11,请使用Math.sqrt(vx * vx + vy * vy)
const nx = vx / len; //归一化线向量。使它成为一个
const ny = vy / len; //像素长。设置比例尺

//变换是x轴的归一化线向量,y在90度
//和原点在线起点
ctx.setTransform(nx, ny,-ny,nx,x1,y1); //设置transform

return len;
}



使用方法



此示例显示了如何使用变换来执行相同的行,但也添加了带注释的长度。



  const canvas = document.createElement( canvas ); const ctx = canvas.getContext( 2d); document.body.append(canvas); ctx.strokeStyle = black; ctx.lineJoin = ctx.lineCap = round; ctx.lineWidth = 3; ctx .font = 16px arial; ctx.textBaseline =中间; ctx.textAlign =中心; const x1 = 40,y1 = 40,x2 = 260,y2 = 110; const endLen = 10;函数setTransformToLine(x1,y1,x2,y2){const vx = x2-x1; const vy = y2-y1; const len = Math.hypot(vx,vy); const nx = vx / len; const ny = vy / len; ctx.setTransform(nx,ny,-ny,nx,x1,y1); return len;} //沿直线设置变换。保持线长//需要保持线len才能获得线末端的x坐标const lineLen = setTransformToLine(x1,y1,x2,y2); const lineLenStr = Math.round(lineLen)+ px; const textWidth = ctx.measureText(lineLenStr).width; const rlen = lineLen-textWidth-16; //在删除文本空间后找到剩余的len行//在行本地坐标中完成渲染//线从(0,0)到(lineLen,0)//现在首先在末端画线,然后沿着textctx.beginPath(); ctx.lineTo(0,-endLen); //开始perp linectx.lineTo(0,endLen); ctx.moveTo(lineLen,-endLen); //行尾位于lineLenctx.lineTo(lineLen,endLen); ctx.moveTo(0,0); //行开始segmentctx.lineTo(rlen / 2,0); ctx.moveTo(lineLen-rlen / 2,0); //线段的结尾ctx.lineTo(lineLen,0); ctx.stroke(); //渲染它.//现在在中心线处添加文本ctx.fillText(lineLenStr,lineLen / 2,0); //若要将变换恢复为其默认设置,请使用恒等矩阵ctx.setTransform(1、0、0、1、0 ,0);  


I'm using HTML canvas to draw Line as shown in the following figure, but this line has an edges on the two sides of it.

As shown in the image the two edges are not perpendicular to the major line.
I tried the following sollutions but it didn't succeed:
* rotate the edges lines, but the rotation will transform them from the original position
* find the angle of the major line and then draw the lines with respect to the line, but this solution was not easy to implement (most likely i implement it wrongly).

this is my code but it will always draw vertical edges:

<!DOCTYPE html>
<html>
<body>

<canvas id="myCanvas" width="200" height="200" style="border:1px solid #d3d3d3;">

<script>

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

var x1 = 100;
var x2 = 150;
var y1 = 50;
var y2 = 120;

ctx.beginPath();
ctx.strokeStyle = "purple";  // Purple path
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);            
ctx.stroke();  // Draw it

ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x1,y1+10);
ctx.stroke();


ctx.restore();
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x1,(y1-10));
ctx.stroke();


ctx.beginPath();
ctx.moveTo(x2,y2);
ctx.lineTo(x2,y2+10);
ctx.stroke();


ctx.restore();
ctx.beginPath();
ctx.moveTo(x2,y2);
ctx.lineTo(x2,(y2-10));
ctx.stroke();


</script>
</body>
</html>

can anyone help me with rotating the two edges lines in order to become perpendicular to the major line. Thanks.

解决方案

Rotate any 2D vector 90deg

Perpendicular lines are simple to calculate if you remember the rotate 90deg rule for a 2D vector.

A vector {x,y} can be rotated 90 deg clockwise {-y,x} or anti clockwise {y,-x}. Swap x and y and negate the y for clockwise or x of anti clock wise

So for a line segment x1,y1 to x2,y2 convert to a vector, normalise that vector and rotate 90 deg as follows

function getPerpOfLine(x1,y1,x2,y2){ // the two points can not be the same
    var nx = x2 - x1;  // as vector
    var ny = y2 - y1;
    const len = Math.sqrt(nx * nx + ny * ny);  // length of line
    nx /= len;  // make one unit long
    ny /= len;  // which we call normalising a vector
    return [-ny, nx]; // return the normal  rotated 90 deg
}

Then say you want to draw a 10 pixel line at the ends of the line segment

const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
document.body.append(canvas);
ctx.strokeStyle = "black";
ctx.lineJoin = ctx.lineCap = "round";
ctx.lineWidth = 3;

// the line segment
const x1 = 40, y1 = 40, x2 = 260, y2 = 110;
const endLen = 10; // length of end lines

var px = y1 - y2; // as vector at 90 deg to the line
var py = x2 - x1;
const len = endLen / Math.hypot(px, py);
px *= len;  // make leng 10 pixels
py *= len; 

// draw line the start cap and end cap.
ctx.beginPath();

ctx.lineTo(x1, y1);   // the line start
ctx.lineTo(x2, y2);
ctx.moveTo(x1 + px, y1 + py); // the start perp line
ctx.lineTo(x1 - px, y1 - py);
ctx.moveTo(x2 + px, y2 + py); // the end perp line
ctx.lineTo(x2 - px, y2 - py);
ctx.stroke();

Update

Simple solution to rendering content along a line, Uses the same 90deg rule.

There is an alternative method of rendering using the same vector rotation, but rather than set the perpendicular axis via vector multiplication, you set the y axis of the transform to 90deg from the x axis which is along the line. Set the origin to the start of the line and you simply render relative to the line.

setTransformToLine(x1, y1, x2, y2)

The following function will set the canvas transform to be along the line

// Set 2D context  current transform along the line x1,y1,x2,y2 and origin to
// start of line. y Axis is rotated clockwise 90 from the line.
// Returns the line length as that is most frequently required when
// using the method saving some time.
function setTransformToLine(x1, y1, x2, y2) {
  const vx = x2 - x1;   // get the line as vector
  const vy = y2 - y1;
  const len = Math.hypot(vx, vy); // For <= IE11 use Math.sqrt(vx * vx + vy * vy)
  const nx = vx / len; // Normalise the line vector. Making it one
  const ny = vy / len; // pixel long. This sets the scale

  // The transform is the normalised line vector for x axis, y at 90 deg 
  // and origin at line start
  ctx.setTransform(nx, ny, -ny, nx, x1, y1); // set transform

  return len;
}

How to use

This example show how to use the transform to do the same line but add annotated length as well.

const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
document.body.append(canvas);
ctx.strokeStyle = "black";
ctx.lineJoin = ctx.lineCap = "round";
ctx.lineWidth = 3;
ctx.font = "16px arial";
ctx.textBaseline = "middle";
ctx.textAlign = "center";

const x1 = 40, y1 = 40, x2 = 260, y2 = 110;
const endLen = 10; 


function setTransformToLine(x1, y1, x2, y2) {
  const vx = x2 - x1; 
  const vy = y2 - y1;
  const len = Math.hypot(vx, vy); 
  const nx = vx / len; 
  const ny = vy / len;
  ctx.setTransform(nx, ny, -ny, nx, x1, y1);
  return len;
}

// Set the transform along the line. Keep the line length
// line len is need to get the x coord of the end of the line
const lineLen = setTransformToLine(x1, y1, x2, y2);

const lineLenStr = Math.round(lineLen) + "px";
const textWidth = ctx.measureText(lineLenStr).width;

const rlen = lineLen - textWidth - 16; // find the remaining line len after removing space for text

// Rendering is done in line local coordinates
// line is from (0,0) to (lineLen,0)

// Now draw the line the ends first and then along the line leaving gap for text
ctx.beginPath();
ctx.lineTo(0, -endLen);             // start perp line
ctx.lineTo(0,  endLen); 

ctx.moveTo(lineLen, -endLen);       // end of line is at lineLen
ctx.lineTo(lineLen,  endLen); 

ctx.moveTo(0,0);                    // line start segment
ctx.lineTo(rlen / 2, 0);

ctx.moveTo(lineLen - rlen / 2,0);   // line end segment
ctx.lineTo(lineLen, 0);

ctx.stroke(); // render it.

// now add text at the line center
ctx.fillText(lineLenStr, lineLen / 2, 0);

// To restore the transform to its default use identity matrix
ctx.setTransform(1, 0, 0, 1, 0, 0);

这篇关于HTML画布绘制线垂直于另一条线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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