前言
写了一些东西,是突然冒出这个想法的。打算用纯前端技术javascript/html/css,构建一个虚拟的世界,就像Minecraft那样。
不知道有多少人还记得 "交互网站" 这个词?这个对于,对于古前端佬来说,应该承载着满满的回忆。
在那个JS仅仅做个跑马灯效果的年代,Flash 可是 PC 端流量的王者。很多很多的品牌官网,都靠着它炫酷、复杂、充满故事感的超高互动性,狠狠地刷了一波存在感!那时候,一个加载页的进度条,都可能是一段精致的动画。搞这个专栏的很多技术,基本都是从里面脱胎的。
本来是想在前言中,给进来的朋友们做个自我介绍,讲讲过往,挂一挂title,顺便装个X。
随后想了想,对比别人的光辉靓丽,自己过往经历虽有波折,但普通的不能再普通了,title挂上我都没眼看。但,气氛毕竟烘托在这里了,那就简单一句话介绍下吧:我从业前端10年+了。

介绍完毕,至于内容优劣,朋友们自行判断了,我只管输出爽自己。
有一些甩锅的话先说
这一系列主要核心是canvas,用到的一些东西,说实话,针对目前大部分的前端工作内容来说,没有多大参考性。
非常有可能看着看着,发现纯纯在浪费自己时间。毕竟目前移动端的流量为主,前端原来的那些狂拽酷炫吊炸天的手段,有点不适宜了。

但我隐隐约约有一个判断,目前对于宽屏幕的需求是渐渐增多的(比如目前的数字大屏应用、宽屏手机等)。
假如未来硬件大升级,浏览器给前端分配的计算和存储资源大大升级,富媒体应用可能再次开花,比如智能眼镜这种的。
打地基
这项工程工作量很大,纯前端手搓虚拟世界听着挺带感,真正实践起来很折磨!
如果对这种从零开始造轮子 的创世过程感兴趣,请务必点个赞、收藏、留言!你的每一个鼓励,都是我把这个系列肝到底的强大动力!
💪💪💪💪!
第一步:建立项目骨架。简简单单,先建个目录!

第二步,上代码。
在这个专栏中,导入JS主要会使用module模式,这样后续抽象、扩展,使用class构建类会比较爽一些。
HTML代码如下:
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>virtual world</title>
<style>
body {
margin: 0;
padding: 0;
}
#world {
display: block;
background-color: #ececee;
}
</style>
</head>
<body>
<canvas id="world"></canvas>
<!-- 便于export导入 -->
<script src="./main.js" type="module"></script>
</body>
</html>
JavaScript代码如下:
js
// main.js
import World from './src/index.js';
const canvas = document.getElementById('world');
const world = new World(canvas,window.innerWidth)
world.display()
js
// /src/index.js
export default class World {
constructor(canvas, width = 600, height = 600) {
this.canvas = canvas;
// 设置宽高
this.canvas.width = width;
this.canvas.height = height;
}
// 用于绘制所有图形
display() {
}
}
好了,不出意外,打开浏览器的画面是这个样子。

0变1,干!
可能很多前端佬你看到创世界这个词眼,脑子里就蹦出是Three.js 、WebGL等这些高大上的词。有这样想法的前端佬请先把思维或者预期再降降,因为主打不引入第三方包,就一点点的开始。
首先,我们创个点。
一个简简单单的Point2D类代码如下:
js
// ./primitives/point2D.js
export default class Point {
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
}
}
嗯,恭喜恭喜,如果跟着到这步骤,重大突破实现,从0就跨越到1了!!!剩下的,就差九千九百九十九万步要走了。哈哈哈~

其实在计算机图形学的世界里,你看到的一切宏大、复杂、逼真... 都是从这个点开始的!当然,三维世界的点会多一个维度z。
至于后续会不会升级到3D。先不急,先让我们把二维世界玩到极致。 毕竟,只有当二维已经装不下你的野心时,升维才更有乐趣。
把点画出来
点的抽象有了,那么借来下简简单单画个点。主要使用的就是canvas的canvas.getContext("2d")。简简单单,使用arc这个方法,画一个圆。
在point2D类上,填上如下方法:
js
// ./src/point2D.js
draw(
ctx,
{
size = 10,
color = 'black',
outline = false,
outlineColor = 'yellow'
} = {},
) {
const rad = size / 2;
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(this.x, this.y, rad, 0, Math.PI * 2);
ctx.fill();
if (outline) {
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = outlineColor;
ctx.arc(this.x, this.y, rad, 0, Math.PI * 2);
ctx.stroke();
}
}
然后我们在/src/index.js中的display方法中,添加上一行:
js
// 用于绘制所有图形
display() {
new Point2D(200, 200).draw(this.ctx);
}
好了,在坐标为{x:200,y:200}画点成功:

canvas这个就不解释了,还有很多很多方法。感兴趣,搜搜,大把大把的资料。
注意!
我们知道canvas的原点{x:0,y:0}是在左上角。而坐标{x:200,y:200},根据上面的绘制方法,坐标是点的中心位置。
所以有用其它绘制点方法的,要注意下自己坐标对应点的位置。不然后续在点转换、线绘制以及很多线路闭合的时候,出现不及预期的效果。
玩一把
点创建出来了,那么就用一个五彩喷绘效果,来结束这第一篇。
我们在/src/index.js中的display方法中,添加上代码:
js
// 随机绘制
for (let i = 0; i < 1000; i++) {
new Point2D(
Math.random() * this.canvas.width,
Math.random() * this.canvas.height
).draw(this.ctx, {
size: Math.random() * (50 - 5) + 5,
color:
`#${Math.floor(Math.random() * 16777215)
.toString(16)
.padStart(6, "0")}`,
});
}

喔,看着艺术成分提高了好几层楼那么高。
源码在这里github.com/Float-none/...
欢迎批评、指正、交流,外加监督!