这是纯前端手搓虚拟世界第二篇。
在上一篇里,我们已经成功搭好了项目的基础骨架,并实现了最基础的单位 ------ Point2D类。如果你是中途加入,点下面先补一下。
戳这里就可以【Virtual World 01】头脑热一把,带你手搓虚拟世界💪!。
吐个槽
第一篇写得有点啰嗦,可能有人觉得我"明明是手搓虚拟世界,为啥从语文课开始讲起"😂。
这...总得有点仪式感嘛。
别急别急!

上代码!!!
来,直接在我们primitives文件夹中,定义一个segment类。
js
// ./primitives/segment.js
export default class Segment {
// p1 p2 传入Point类
constructor(p1, p2) {
this.p1 = p1;
this.p2 = p2;
}
}
现在看过去,这个类依旧那么简单,但如果能跟到后面,你会发现---它是构建虚拟世界里所有「形状」的基础大到建筑、道路、地形轮廓,小到网格、辅助线,全都绕不开它。
打基础的时候就是这么简单,但往往复杂的东西就是简单东西堆砌的。知道1+1然后考上了清华的朋友一定会给我点个赞的。

骑驴看账本--走着瞧吧
两点一线
对于创建虚拟世界,我们打小就有理论基础的。这个我坚信。
你肯定还记得小学二年级的王老师曾告诉你,两点才能成一条线!!!
嗯,好,知识充,接下来动手实操了。技术点上,主要还依赖canvas的getContext("2d")。其中有moveTo 和 lineTo。
在Segment类中填上如下代码:
js
// ./primitives/segment.js
draw(ctx, { width = 2, color = "black" } = {}) {
ctx.beginPath(); // 开始绘制
ctx.lineWidth = width; // 设置线的宽度
ctx.strokeStyle = color; // 设置线的颜色
ctx.moveTo(this.p1.x, this.p1.y);
ctx.lineTo(this.p2.x, this.p2.y);
ctx.stroke();
}
代码很好理解,但我们可以靠想象力丰富下。
脑海中想一下你拿着笔,先移动笔到纸的某一点,然后下笔,手腕将笔移动到另一点,把笔拿开,线搞定!!
show一下,在/src/index.js中的display方法添加如下代码:
js
// 用于绘制所有图形
display() {
new Segment(new Point2D(200, 200),new Point2D(400, 400)).draw(this.ctx)
}

嗯...万里长征第二步。
粗细是个问题(但先不急)
先说明下:
| 属性 | 说明 |
|---|---|
lineWidth |
Canvas 只是大致控制粗细,不是像 SVG 那样精确 |
strokeStyle |
支持颜色、渐变等 |
| 高级需求 | 需要结合像素密度 / DPR 做适配 |
目前好只是基础几何阶段先不在乎这个。后面做 坐标系 + 视口缩放 的时候,我们再一起处理 DPI 适配和缩放视觉一致性。
至于曲线,先忘掉这回事吧。
装个X
老师常常告诫我们举一反三,嗯,这就来了。
我们目前两个类:
- Point2D
- Segment
但理论上,可以绘制任何几何图形了,这样,简简单单搞个分形树。
还在在/src/index.js中的display方法中,添加上实验代码:
js
// 用于绘制所有图形
const fractalTree = (p1, length, angle, depth, ctx) => {
if (depth <= 0) return;
const p2 = new Point2D(
p1.x + Math.cos(angle) * length,
p1.y + Math.sin(angle) * length
);
new Segment(p1, p2).draw(this.ctx);
const nextLength = length * 0.7;
fractalTree(p2, nextLength, angle - 0.5, depth - 1, ctx); // 左叉
fractalTree(p2, nextLength, angle + 0.5, depth - 1, ctx); // 右叉
}
fractalTree(new Point2D(300, 500), 120, -Math.PI / 2, 10, this.ctx);
不出意外,艺术成分还得上几层楼。

今天就这样了!!!