题记:
最近项目里遇到个小需求,就是我们会存储一些
3D(ThreeJs、BabylonJS)的位置坐标(x,y,z)的值,然后存储下一段 3D的运动轨迹。但是想要快速预览下 这段存储数据是什么效果,以供选择。简单记录下实现过程。
分析:
通过了解了需求后,我的想法是只需要将 3D 坐标能使用 Cavans 绘制出来,然后通过计时器来补间绘制到画布上,那记录的运动轨迹其实就可以绘制出来了。
那我们需要实现:
- 绘制 3D 坐标轴
- 将 3D 的坐标点数据转换成 2D 的坐标点数据,绘制上去。
- 定时器补间绘制。
最终:效果大致如下:
第一步:绘制坐标轴
坐标轴中,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)
如何绘制小圆点。
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);
简单记录了下~