在JavaScript Canvas中沿着一条线移动一个点

16
假设我有一条线的坐标(25,35 45,65, 30,85 - 它是由两部分构成的),我需要在每个帧上以恒定距离沿着该线移动一个点(汽车)。我应该如何实现这个功能?

你知道如何以恒定的速度沿着单个线段移动它吗? - Beta
不,但是一旦我能够沿着单行线段移动,就可以轻松地从前一行的末尾重复该过程。 - Conner Ruhl
你怎么只用一行代码就实现了呢? - Conner Ruhl
4个回答

24

你有两条线的坐标 (25,35) (45,65) (30,85),你想移动的点会被放置在这些坐标中的第一个位置 (25,35),并希望它向第二个坐标 (45,65)(第一条线段的末端)移动。

第一步是确定点要移动的方向,方向是点位置和目标位置之间的角度。为了找到这个角度,你可以使用 Math.atan2(),将目标位置 Y - 点位置 Y 作为第一个参数传入,将目标位置 X - 点位置 X 作为第二个参数传入。

var Point = {X: 25, Y: 35};
var Target = {X:45, Y:65};

var Angle = Math.atan2(Target.Y - Point.Y, Target.X - Point.X);

现在获取该角度的正弦值和余弦值,正弦值是沿Y轴移动的值,而余弦值是沿X轴移动的值。将正弦值和余弦值乘以每帧要移动的距离。

var Per_Frame_Distance = 2;
var Sin = Math.sin(Angle) * Per_Frame_Distance;
var Cos = Math.cos(Angle) * Per_Frame_Distance;

好的,现在要做的就是设置重新绘制方法,在每次调用时将正弦添加到点的Y位置,将余弦添加到点的X位置。检查点是否已经到达目的地,然后对第二条线段的末端执行相同的过程以向其移动。


1
感谢大家提供的出色答案!这是我在Stack Overflow上的第一个问题,很高兴看到程序员们可以使用如此好的资源!再次感谢,C.Ruhl - Conner Ruhl
优秀的答案。 - Kosta Kontos

16

虽然晚了8年,但有人可能会觉得这很有用。这种方法更快,因为它不使用atan、cos、sin和平方根等缓慢的函数。

function getPositionAlongTheLine(x1, y1, x2, y2, percentage) {
    return {x : x1 * (1.0 - percentage) + x2 * percentage, y : y1 * (1.0 - percentage) + y2 * percentage};
}

通过率作为0到1之间的数值,其中0是线的起点,1是终点。

var xy = getPositionAlongTheLine(100, 200, 500, 666, 0.5);
console.log(xy.x, xy.y);

正是我正在寻找的 :) - DigitalWM
太好了!这非常有用。我将在我正在开发的可视化工具中使用它 :) 谢谢。 - echoaman
简单而有效,非常感谢 - Fennec

14
考虑线段 (25,35 45,65)。从起点到终点的向量是 (20, 30)。为了按照这个方向移动点 (x,y),我们可以将其加上该向量:
V = (20, 30) (x,y) => (x+20, y+30)。
如果我们从线段的起点开始,我们将到达终点。但这步太大了。我们想要一个更小但同样方向的步骤,因此我们将向量乘以,比如说,0.1:
V = (2, 3) (x,y) => (x+2, y+3) => (x+4, y+6) => ...
我们方便地进行标准化,使其长度为1,方法是除以它的长度:
V => V/|V| = (2,3)/sqrt(22 + 32) = (7.21, 10.82)
然后,您只需将其乘以所需的步长即可。

啊,我知道应该有更简单的方法! - Delta
非常酷!我之前以为这可以用向量实现,但是我无法弄明白。 - Conner Ruhl
我知道你的方法是可行的,但我很难理解你的符号表示法。你能否用伪JavaScript重写那三行代码?我只是九年级的学生,你的数学超出了我的水平 :) 谢谢! - Conner Ruhl
4
为了使给定向量(X,Y),在这种情况下为(20,30)正常化,您需要先获取该向量的长度(或大小),然后将其分量除以该长度。 长度为Math.sqrt(X*X + Y*Y)。 因此,(20,30)的长度为Math.sqrt(20*20 + 30*30),(20,30)规范化后将变为(20 / length, 30 / length) - Delta
好的,明白了!那完全回答了我的问题! - Conner Ruhl

2
有时候将数学公式转化为代码并不是那么显而易见。以下是一个函数的实现,可以将指定距离沿着一条直线移动一个点。它使用向量表示法:
function travel(x, y, dx, x1, y1, x2, y2)
{
    var point = new Vector(x, y),
        begin = new Vector(x1, y1),
        end = new Vector(x2, y2);
    return end.sub(begin).norm().mul(dx).add(point);
}

class Vector
{
    constructor(x = 0, y = 0) {
        this.x = x;
        this.y = y;
    }

    clone() {
        return new this.constructor(this.x, this.y);
    }

    add(v) {
        this.x += v.x;
        this.y += v.y;
        return this;
    }

    sub(v) {
        this.x = this.x - v.x;
        this.y = this.y - v.y;
        return this;
    }

    mul(x) {
        this.x *= x;
        this.y *= x;
        return this;
    }

    div(x) {
        this.x /= x;
        this.y /= x;
        return this;
    }

    get mag() {
        return Math.sqrt(this.x * this.x + this.y * this.y);
    }

    norm() {
        var mag = this.mag;
        if (mag > 0) {
            this.div(mag);
        }
        return this;
    }
}

另外还有一种没有使用 Vector 类的版本:

function travel(x, y, dx, x1, y1, x2, y2)
{
    var a = {x: x2 - x1, y: y2 - y1},
        mag = Math.sqrt(a.x*a.x + a.y*a.y);
    if (mag == 0) {
        a.x = a.y = 0;
    }
    else {
        a.x = a.x/mag*dx;
        a.y = a.y/mag*dx;
    }
    return {x: x + a.x, y: y + a.y};
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接