【WEB】 使用 Canvas 绘制 3D 坐标运动轨迹

306 阅读2分钟

题记:

最近项目里遇到个小需求,就是我们会存储一些3D(ThreeJs、BabylonJS) 的位置坐标(x,y,z)的值,然后存储下一段 3D的运动轨迹。但是想要快速预览下 这段存储数据是什么效果,以供选择。简单记录下实现过程。

分析:

通过了解了需求后,我的想法是只需要将 3D 坐标能使用 Cavans 绘制出来,然后通过计时器来补间绘制到画布上,那记录的运动轨迹其实就可以绘制出来了。

那我们需要实现:

  • 绘制 3D 坐标轴
  • 将 3D 的坐标点数据转换成 2D 的坐标点数据,绘制上去。
  • 定时器补间绘制。

最终:效果大致如下: 20230731095616_rec_.gif

第一步:绘制坐标轴

坐标轴中,X 和 Y 轴其实就是画两条垂直直线,这没什么好说的。Z 轴也不想搞太复杂,毕竟不需要调视角,所以从原点绘制到左下角就可以了。

控制好原点位置,让它呈现的视野多点就好了。

    //绘制坐标系
    private drawCoordinateSystem(ctx: CanvasRenderingContext2D) {
        ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
        const originX = ctx.canvas.width / 3;
        const originY = ctx.canvas.height / 2;

        //绘制坐标
        ctx.beginPath();
        ctx.moveTo(originX, originY);
        ctx.lineTo(ctx.canvas.width, originY);
        ctx.strokeStyle = "red";
        ctx.stroke();

        //Y 轴
        ctx.beginPath();
        ctx.moveTo(originX, originY);
        ctx.lineTo(originX, -ctx.canvas.height);
        ctx.strokeStyle = "green";
        ctx.stroke();
        //Z 轴
        ctx.beginPath();
        ctx.moveTo(originX, originY);
        ctx.lineTo(0, ctx.canvas.height);
        ctx.strokeStyle = "blue";
        ctx.stroke();
    }

第二步:将 3D 坐标点转换成 2D 坐标点

3D 坐标点数据其实就是(x,y,z)。其中 x 和 y 很好确认,就是从原点沿着轴向进行增减就可以了。直接设值就行了。就是 z 的值需要通过三角函数计算下。

如图所示,我们只需要求出这个夹角的角度。然后通过三角函数就可以知道这个 z 将会影响 2D 中的偏移值,最后用 x、y 加上偏移的值就是最终 2D 中所在的 x、y 的坐标。

而这个夹角其实根据我黄线所画的邻边对边就可以求出来。

 //求角度
 const gamma = Math.round((Math.atan(对边 / 邻边) * 180) / Math.PI);
  //求弧度
 const radian = ((2 * Math.PI) / 360) * gamma;
 
 //求X 轴应 Z 轴影响的偏移值
  z * Math.sin(radian)
  
  //求Y 轴应 Z 轴影响的偏移值
  z * Math.sin(radian)
  

image.png

如何绘制小圆点。

ctx.beginPath();
ctx.arc( X 的值,  Y 的值, 半径, 0, 2 * Math.PI);
ctx.fillStyle = "red";
ctx.fill();

最终绘制 3D 坐标点的函数。 其中 scale 是为了让3D 的传入值绘制到 2D 面板上看的更清晰。 另外新增的 size 是为了让绘制的小圆点会因为 z 轴有放大缩小的变换效果。

private drawPoint(ctx: CanvasRenderingContext2D, x: number, y: number, z: number, scale: number) {
        const centerX = ctx.canvas.width / 3;
        const centerY = ctx.canvas.height / 2;
        let pointX = centerX + x * scale;
        let pointY = centerY - y * scale;

        let size = 5; //大小
        if (z != 0) {
            const gamma = Math.round((Math.atan(centerX / centerY) * 180) / Math.PI);
            let radian = ((2 * Math.PI) / 360) * gamma;
            pointX -= scale * z * Math.sin(radian);
            pointY += scale * z * Math.cos(radian);

            size += z;
        }

        ctx.beginPath();
        ctx.arc(pointX, pointY, size, 0, 2 * Math.PI);
        ctx.fillStyle = "red";
        ctx.fill();
    }Ï

第三步: 定时绘制即可

通过setInterval实现即可。

 const id = setInterval(() => {
            this.drawCoordinateSystem(ctx);
            const { position } = 获取3D 的坐标位置信息;
            this.drawPoint(ctx, position.x, position.y, position.z, scale);
  }, 20);

简单记录了下~

Bye~