在做和做好是两码事,在这个人均摆烂的时代,前者足以感动自己了。
写在最前
今天短视频刷着刷着,刷到了Uzi要复出加入EDG的视频,刚开始看到熟悉的营销号的开场白感觉一眼假,于是乎我像往常一样打开评论区看看有没有比视频更精彩的评论,但刷着刷着感觉不对劲了怎么上热搜了啊,真复出了,贴吧不得狂欢人人升三级?期待一波。
扯远了,回到正题,本文将使用 threejs 实现 虚拟摇杆遨游星空的交互,通过本文你能学习到如何的实现虚拟摇杆、粒子效果、按键操控运动等知识,并且内容知识非常简单,相信你看完本文内容后自己动手也能实现美丽的效果。
虽说是狠久之前的学习DEMO,但稍微加工一下效果也是很NICE的~ )。
话不多说,直接开冲!
美丽的粒子效果 ??
粒子效果可以说是很多前端小伙伴入 Web3D 的理由之一,非常炫酷。
今天我们通过物体点 Points + 自定义几何体 BufferGeometry 来实现简单的星空场景
原理非常简单: 将镜头设置在一个超大的点物体内
在THREEJS中物体是通过材质和几何体构成的。
点材质
点材质是点物体默认使用的材质。他的参数值跟其他材质类似,你可以设置点的大小、颜色甚至还可以贴贴图 ~
const material = new THREE.PointsMaterial({
color: '#ffff00',
size: 2,
transparent: true,
opacity: 0.5,
});
自定义几何体
THREEJs为我们封装了非常多的网格几何体,我们也有自定义形状的需求,这个时候就需要用到BufferGeometry。从之前的文章可以了解到,几何体由无数的点形成的,所以需要提供顶点位置与颜色数据。下面是一个通过随机点生成正方体的案例。
let vertexPoint: number[] = [];
let size = 1000;
for (let i = 0; i < 10000; i++) {
let x, y, z;
x = Math.random() * (size) - 1000 / 2;
y = Math.random() * (size) - 1000 / 2;
z = Math.random() * (size) - 1000 / 2;
vertexPoint.push(x);
vertexPoint.push(y);
vertexPoint.push(z);
}
let geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(vertexPoint), 3));
因为一个点需要在3D 空间中确定位置,需要设置x、y、z的3个顶点坐标。
我们通过循环生成 10000个 x、y、z方向 -500 到 500 之间的顶点数据,以此生成正方体的粒子效果。
你可以脑洞打开,生成点是最基础的,比如可以9个顶点生成三角形、12个顶点生成矩形,只要你能将点摆放好位置,你能生成各种各样的图形,非常自由~
也可以为每个点设置不同的颜色。
geometry.setAttribute('color', new THREE.BufferAttribute(colorPointer, 3))
和 WebGL Shader 是不是非常像。
最终我们通过Points生成物体
point = new THREE.Points(geometry, material)
星空效果
再设置物体绕Y轴自转,于是效果如下:
谁不会为星点天空着迷呢?
虚拟摇杆
相信大家比我还懂什么是虚拟摇杆,他的实现方式也非常的简单,各位实现交互肯定不在话下。
拖拽 + 判断是否在圆内
拖拽无需多说,判断点是否在园内,只需要使用勾股定理
判断x到圆心的距离平方与y轴到圆心的距离平方是否小于半径的平方。
(Math.abs(x) ^ 2) + (Math.abs(y) ^ 2) < (r ^ 2)
优化: 我们知道,摇杆在拖拽滑动脱离摇杆区域时,点会停靠在中心点到当前点的线段与圆相交的地方。所以也是求中心点到当前点与中心点的X轴形成的夹角。
这时候我们的三角函数又入场了。
Math.atan2
文档写的非常详细,这里就不水文字了,直接贴代码
angle = (Math.atan2(-y, x) * 180) / Math.PI;
angle = angle < 0 ? angle + 360 : angle; //-180 - 180 to 0 - 360
虚拟摇杆逻辑源码如下
摇杆源码
let dragControl = (e: MouseEvent) => {
let steeringWheel = document.querySelector(".steering-wheel") as HTMLElement;
let controlDot = document.querySelector(".control-dot") as HTMLElement;
if (controlDot instanceof HTMLElement) {
let {
top: ft,
left: fl,
height: fh,
width: fw,
} = steeringWheel.getBoundingClientRect();
window.onmousemove = (mouse) => {
let centerPoint = {
y: ft + fh / 2,
x: fl + fw / 2,
};
let x = mouse.pageX - centerPoint.x;
let y = mouse.pageY - centerPoint.y;
let r = fh / 2;
const judgePointInCircle = () => {
if ((Math.abs(x) ^ 2) + (Math.abs(y) ^ 2) < (r ^ 2)) {
return true;
} else {
return false;
}
};
angle = (Math.atan2(-y, x) * 180) / Math.PI;
angle = angle < 0 ? angle + 360 : angle;
let isInCircle = judgePointInCircle();
if (isInCircle) {
controlDot.style.left = mouse.pageX - fl + "px";
controlDot.style.top = mouse.pageY - ft + "px";
} else {
let x = Math.cos((angle * Math.PI) / 180) * r;
let y = Math.sin((angle * Math.PI) / 180) * r;
controlDot.style.left = x + r + "px";
controlDot.style.top = -y + r + "px";
}
};
window.onmouseup = () => {
controlDot.style.left = "50%";
controlDot.style.top = "50%";
angle = 0;
window.onmousemove = null;
window.onmouseup = null;
};
}
};
当算出角度后,接下来的操作都一气呵成了。
剩下的都是可自由发挥的内容,我这里举个小例子,控制小球移动。
虽然小球朴素了一些,但整个效果也是被漂亮的星空包裹着,瑕不掩瑜~
结尾
我们通过本文学习到了,如何渲染星空、一些2D 几何的操作和判断等。
碎碎念
每次回到家都是以一种半清醒的状态躺着刷短视频刷个1个多小时,中途多次告诉自己该做事了,可是大拇指向上滑的功夫是一点没落下,得想个办法保持回家时的状态。。回家前先洗澡?这可能是个好办法,明天试试。
各位帅哥美女们点点赞呀,你人还怪好的嘞,每次点赞都能给我无穷大的动力!!!
夜深了,困~