Javascript canvas lineTo,想要在一定距离后停止

时间:2017-01-18 13:34:38

标签: javascript canvas

我正在开展一个单独的项目,我正试图从炮塔到敌人画一条线。但我希望这条线保持在炮塔的边界内,而不是一直延伸到敌人。我在下面的代码片段中找到了一个较小的示例。

请彻底解答,因为我绝对不是专家。非常感谢!感谢。

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

var turret = {x:20, y:20, w:20, h:20};

var randomBox = {x:30+Math.random() * 300, y:30+Math.random() * 300, w:40, h:40};

c.fillStyle = "red";
c.fillRect(turret.x, turret.y, turret.w, turret.h);

c.fillStyle = "yellow";
c.fillRect(randomBox.x, randomBox.y, randomBox.w, randomBox.h);


c.beginPath();
c.moveTo(turret.x + (turret.w/2), turret.y + (turret.h/2));
c.lineTo(randomBox.x, randomBox.y);
c.stroke(); 
#canvas {
  background-color: #eee;
  
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <canvas id="canvas" width=500 height=500></canvas>
</body>
</html>

1 个答案:

答案 0 :(得分:1)

矢量数学。

一条线可以被认为是一个坐标和一个矢量,该矢量描述了从起始坐标到终点的方向和距离。当您通过其端点描述一条线时,您使用的是2个坐标。通常我们将一个以开头和结尾为线的线称为线段,而不是实际上无限长的线。

线段为坐标

 var x1 = 100;
 var y1 = 100;
 var x2 = 300;
 var y2 = 400;

线段的矢量

要获取直线的矢量分量,只需从终点减去起点即可。向量是各种类型的线段,它们的起始点始终位于坐标0,0。

 var vx = x2 - x1;  // v for vector
 var vy = y2 - y1;

幅度或长度

所有向量的大小(或长度)都是由于计算长度而总是正的。矢量的x和y分量构成三角形的两个短边,沿矢量的线是最长的线,即斜边。

我们使用毕达哥拉斯来计算向量幅度。

var mag = Math.sqrt(vx * vx + vy * vy); // mag for magnitude

标准化矢量

在许多关于线条的情况下(比如在这种情况下),我们对线条的长度并不是那么感兴趣,而只是方向。为了简化计算,我们喜欢使用一种称为单位向量的特殊形式的向量。这是一个正好长1个单位的向量(在我们的例子中,1是单位是一个像素)

单位矢量具有许多在执行几何时非常有用的属性。

创建单位向量的过程称为规范化。它只是通过将矢量的x,y部分除以其总长度(如上所计算)来完成的

// normalise vector
var nx = vx / mag;  // n for normal
var ny = vy / mag;

缩放正常

现在你有了表示直线方向的单位向量,很容易找到距离起点固定距离的点。就像在数字线上一样,如果你想要一个点10个单位,比如5,你可以添加10到5.但它也与10“单位”到5或10 * 1 + 5相同。在几何中,单位是单位矢量。

因此,如果我们想要从开始点开始10个单位(像素),我们将单位向量的两个分量乘以10,并将它们添加到起始坐标。

var endX = x1 + nx * 10;
var endY = y1 + ny * 10;

现在是endX,endY距离线路起点10个像素。

矢量数学。

正如您所看到的那样,使用单位矢量可以轻松地获得距离起点任何距离的任何点。

所有矢量库都包括矢量和坐标的上述操作。我们将一组两个坐标(x,y)称为2D矢量vec,我们有add()sub()multiply()divide(),{ {1}},magnitude()以及一些同样方便的操作,例如normalise()cross(),但目前这个问题超出了这个范围。

如果您对编写游戏感兴趣,我强烈建议您编写自己的小型2D矢量库,创建时间不应超过一小时(如果您不熟悉编程,可能需要两个小时),并且您将学习一些必要的数学几乎任何类型的游戏所需的技能(BTW 3D矢量数学几乎与2D相同,只有额外的组件称为z)。

解决您的问题

我复制了您的代码并进行了一些更改。我还在上面描述的数学中做了几个捷径。与任何一组公式一样,如果将它们放在一起,您可以简化它们以获得更有效和易于理解的公式。在游戏中,这是一件特别容易的事情,你可能每秒进行数百万到数百万次的计算。

函数dot()使用线段x1,y1,x2,y2和值maxLen绘制一条长度不超过drawLineLength像素的线。

如果您有疑问,请在评论中提问。

您的代码已针对您的问题进行了调整。

maxLen
var canvas = document.getElementById("canvas");
var c = canvas.getContext("2d");
c.lineWidth = 4;
c.lineCap = "round";
var turret = {x:20, y:20, w:20, h:20};
const maxLineLength = 50; // max length of line in pixels
var rBox = {              // FKA randomBox
     x : 0,
     y : 0, 
     w : 40, 
     h : 40
};

// drawLineLength  draws a line with maximum length
// x1,y1 from
// x2,y2  line to
// maxLen the maximum length of the line. If line is shorter it will not be changed
function drawLineLength (x1, y1, x2, y2, maxLen) {
    var vx = x2 - x1; // get dist between start and end of line
    var vy = y2 - y1; // for x and y

    // use pythagoras to get line total length
    var mag = Math.sqrt(vx * vx + vy * vy); 
    if (mag > maxLen) { // is the line longer than needed?

        // calculate how much to scale the line to get the correct distance
        mag = maxLen / mag;
        vx *= mag;
        vy *= mag; 
    }
    c.beginPath();
    c.moveTo(x1, y1);
    c.lineTo(x1 + vx, y1 + vy);
    c.stroke();
}
// Test function is on a timer and run every 500ms (1/2 second)
function doTest () {
    // clear the canvas of last render
    c.clearRect(0, 0, canvas.width, canvas.height);

    // find a random position for the box inside the canvas and 10 pixels from any edge
    rBox.x = 10 + Math.random() * (canvas.width - rBox.w - 20);
    rBox.y = 10 + Math.random() * (canvas.height - rBox.h - 20); 
   
    // draw both objects
    c.fillStyle = "red";
    c.fillRect(turret.x, turret.y, turret.w, turret.h);

    c.fillStyle = "yellow";
    c.fillRect(rBox.x, rBox.y, rBox.w, rBox.h);

    // call function to draw line that will have a maximum length
    drawLineLength(
        turret.x + turret.w / 2, 
        turret.y + turret.h / 2,
        rBox.x + rBox.w / 2,
        rBox.y + rBox.h / 2,
        maxLineLength
    );
    setTimeout(doTest,500);
}
doTest();
#canvas { background-color: #eee;  }