【Virtual World 02】两点一线!!!

这是纯前端手搓虚拟世界第二篇。

在上一篇里,我们已经成功搭好了项目的基础骨架,并实现了最基础的单位 ------ 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然后考上了清华的朋友一定会给我点个赞的。

骑驴看账本--走着瞧吧

两点一线

对于创建虚拟世界,我们打小就有理论基础的。这个我坚信。

你肯定还记得小学二年级的王老师曾告诉你,两点才能成一条线!!!

嗯,好,知识充,接下来动手实操了。技术点上,主要还依赖canvasgetContext("2d")。其中有moveTolineTo

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

不出意外,艺术成分还得上几层楼。

今天就这样了!!!

相关推荐
anOnion11 小时前
Agentic 前端开发之 实时显示 AI Agent 终端输出
前端·javascript·人工智能
这是个栗子11 小时前
【前端性能优化】优化数据加载:用 Promise.all 从串行到并行
前端·javascript·性能优化·异步编程·前端优化·promise.all
fei_sun11 小时前
黑洞路由(Null Route/空接口路由)
服务器·前端·javascript
摇滚侠13 小时前
方法 A 等方法 B 执行完再执行 叫同步调用还是异步调用 JS 默认是同步调用还是异步调用
开发语言·javascript·ecmascript
触底反弹14 小时前
🔥 字符串算法面试三连击:反转、回文、回文变种,搞懂这三题稳了!
前端·javascript·算法
触底反弹14 小时前
AI Tool Use 深度解析:大模型是如何"突破物理限制"调用外部工具的?
javascript·人工智能·后端
程序边界14 小时前
lac_agent自愈链路下篇——从systemd托管到真正的自愈
前端·javascript·chrome
竹林81814 小时前
从 RPC 超时到批量签名:我用 @solana/web3.js 重构了一个 NFT 铸造页面,踩了这些坑
前端·javascript
优雅格子衫14 小时前
TypeScript 类的基本使用小结
javascript·ubuntu·typescript
橘子星14 小时前
从零手写 RAG 语义检索:基于 Node.js 实现轻量级向量搜索
javascript·人工智能