在JavaScript画布中沿直线移动点

在JavaScript画布中沿直线移动点,javascript,math,canvas,2d,line,Javascript,Math,Canvas,2d,Line,假设我有一条线的坐标(25,35,45,65,30,85-这是一条由两部分组成的线)。我需要在每帧沿该线以恒定距离移动一个点(汽车)。我该怎么做呢?嘿,你有两条线的坐标(25,35)(45,65)(30,85),你要移动的点将被放置在这些坐标(25,35)中的第一个,你希望它向第二个坐标(45,65)(第一条线段的末端)移动 第一步是获取点将要移动的方向,方向是点位置和目标位置之间的角度。要找到该角度,可以使用数学.atan2(),将目标位置Y-点位置Y作为第一个参数传入,并将目标位置X-点位置

假设我有一条线的坐标(25,35,45,65,30,85-这是一条由两部分组成的线)。我需要在每帧沿该线以恒定距离移动一个点(汽车)。我该怎么做呢?

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

第一步是获取点将要移动的方向,方向是点位置和目标位置之间的角度。要找到该角度,可以使用
数学.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位置。检查该点是否已到达其目的地,然后执行相同的过程向第二条线段的末端移动。

考虑该线段(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)


然后,你可以将它乘以你想要的步长。

有时,如何将数学公式转换成代码并不那么明显。以下是一个函数的实现,该函数沿直线移动指定距离的点。它使用向量表示法:

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;
    }
}
以及不带
矢量的版本
类:

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

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

你知道如何在一条直线上匀速移动吗?不知道,但只要我能在一条直线上移动,就可以很容易地从上一条直线的末尾重复这个过程。那么你如何在一条直线上移动呢?啊,我知道应该有一个更简单的方法!很酷!我认为使用向量是可能的,但O无法理解。我知道你的方法有效,但我很难理解你的符号。你能用伪JavaScript重写这三行吗?我才九年级,你的数学比我高:)谢谢!要规范化给定向量(X,Y),在本例中为(20,30),首先获得该向量的长度(或幅值),然后将分量除以该长度。长度为
Math.sqrt(X*X+Y*Y)
。所以(20,30)长度是
Math.sqrt(20*20+30*30)
和(20,30)归一化将是
(20/length,30/length)
好的,明白了!这完全回答了我的问题!谢谢大家的精彩回答!这是我的第一个堆栈溢出问题,我很高兴看到这样一个伟大的资源可供程序员使用!再次感谢,C.鲁勒回答得很好。正是我想要的:)太好了!这是非常有用的。我将使用这个作为我正在使用的可视化工具:)谢谢。