前端小练习——星辰宇宙(JS没有上限!!!)

前端小练习——星辰宇宙(JS没有上限!!!)

        前言:在刚开始学习前端的时候,我们会学习到前端三件套中的JavaScript,可能那时候读者没有觉得JavaScript这个语言有多么的牛逼,本篇文章将会使用一个炫酷的案例来刷新你对JavaScript这个语言的认知与理解。




✨✨✨这里是秋刀鱼不做梦的BLOG

✨✨✨想要了解更多内容可以访问我的主页
秋刀鱼不做梦-ZEEKLOG博客

先让我们看一下最终的结果:

在开始讲解这个炫酷的案例之前,先让我们了解一下本案例所需的前置知识:

Three.js:一个用于创建和显示3D图形的JavaScript库。代码中导入了Three.js的核心库和轨道控制库(OrbitControls),用于处理3D场景的创建和相机控制。WebGL:用于在网页中绘制3D图形的底层API。Three.js封装了WebGL,使得3D渲染变得更简单。模块化 JavaScript:使用 ES6 的模块导入语法 (import) 引入外部库,使代码结构更加清晰。着色器编程:自定义顶点和片段着色器,通过 onBeforeCompile 方法替换默认着色器,控制点的大小、颜色和运动效果。缓冲几何体:使用 BufferGeometry 来管理大量点的性能,提升渲染效率。动画循环:利用 setAnimationLoop 实现流畅的渲染动画,每帧更新控制器状态和场景渲染。响应式设计:通过 resize 事件监听器,动态调整相机和渲染器的尺寸,以适应浏览器窗口的变化。

        ——在文章的最后,我们给出了实现这个案例的全部代码,读者可以直接复制后在自己的编译器上执行一下!!!

        那么现在正式开始我们的讲解:

目录

1.导入库和清理控制台

2.创建场景和相机

3.创建渲染器

4. 处理窗口大小变化

5. 控制器设置

6. 创建全局uniform变量和点的属性

7. 创建点的顶点数组

 8. 添加围绕的点

9. 创建几何体和材质

10. 创建点云并添加到场景

11. 渲染循环

——全部代码


1.导入库和清理控制台

        开始我们要先导入库和清理控制台:

import * as THREE from "https://cdn.skypack.dev/[email protected]"; // 导入三维模型库 import { OrbitControls } from "https://cdn.skypack.dev/[email protected]/examples/jsm/controls/OrbitControls"; // 导入轨道控制库 console.clear(); // 清除控制台 

        注释:这部分代码导入了Three.js库及其轨道控制功能,并清理了控制台,以便后续输出信息更清晰。

2.创建场景和相机

        在导入库和清理控制台后,我们就需要创建场景和相机:

let scene = new THREE.Scene(); // 创建场景 scene.background = new THREE.Color(0x160016); // 设置场景背景颜色 let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000); // 创建相机 camera.position.set(0, 4, 21); // 设置相机位置 

        注释:这部分代码创建了一个场景和一个透视相机,设置了相机的位置和背景颜色,为后续的渲染准备基础环境。

3.创建渲染器

        其后我们需要为其创建一个渲染器:

let renderer = new THREE.WebGLRenderer(); // 创建渲染器 renderer.setSize(innerWidth, innerHeight); // 设置渲染器大小 document.body.appendChild(renderer.domElement); // 把渲染器加入到页面中 

        注释:这里创建了一个WebGL渲染器,并设置其大小为窗口的宽高,最后将渲染器的DOM元素添加到网页中,以显示渲染结果。

4. 处理窗口大小变化

        在创建完渲染器之后,我们需要对后续的窗口的大小的变化进行处理:

window.addEventListener("resize", event => { camera.aspect = innerWidth / innerHeight; // 更新相机的长宽比 camera.updateProjectionMatrix(); // 更新相机的投影矩阵 renderer.setSize(innerWidth, innerHeight); // 设置渲染器大小 }); 

        注释:这一部分代码监听窗口的大小变化,动态调整相机的长宽比和渲染器的大小,确保在窗口大小变化时,场景仍能正确显示。

5. 控制器设置

        处理完窗口大小的变化之后,我们就需要对控制器进行设置了!

let controls = new OrbitControls(camera, renderer.domElement); // 创建控制器 controls.enableDamping = true; // 开启阻尼效果 controls.enablePan; // 禁用平移(此行未完成) 

        注释:这部分代码创建了一个轨道控制器,允许用户通过鼠标控制相机旋转和缩放,增强用户交互体验。注意,这里 enablePan 需要完整设置。

6. 创建全局uniform变量和点的属性

        接下来让哦我们对全局的uniform变量和点的属性进行设置:

let gu = { time: { value: 0 } }; // 创建全局uniform变量 let sizes = []; // 点的大小数组 let shift = []; // 点的移动数组 let pushShift = () => { // 创建移动函数 shift.push( Math.random() * Math.PI, Math.random() * Math.PI * 2, (Math.random() * 0.9 + 0.1) * Math.PI * 0.1, Math.random() * 0.9 + 0.1 ); // 随机生成位置信息 }; 

        注释:定义了一个全局uniform变量 gu 用于时间控制,并初始化了两个数组 sizesshift 用于存储点的大小和位置信息。同时,定义了一个函数 pushShift,用于生成随机的位移数据。

7. 创建点的顶点数组

        接下来让我们创建点的顶点数组:

let pts = new Array(50000).fill().map(p => { sizes.push(Math.random() * 1.5 + 0.5); // 添加随机大小 pushShift(); // 添加位置信息 return new THREE.Vector3().randomDirection().multiplyScalar(Math.random() * 0.5 + 9.5); // 返回随机方向的点 }); 

        注释:这里创建了一个包含5万个点的数组 pts,每个点都有随机的大小和方向。通过 randomDirection() 方法生成随机方向的向量,模拟球体内的点。

 8. 添加围绕的点

        创建完点的顶点数组之后,让我们添加其围绕的点:       

for (let i = 0; i < 100000; i++) { let r = 10, R = 40; // 定义内外半径 let rand = Math.pow(Math.random(), 1.5); // 生成随机数 let radius = Math.sqrt(R * R * rand + (1 - rand) * r * r); // 计算随机半径 pts.push(new THREE.Vector3().setFromCylindricalCoords(radius, Math.random() * 2 * Math.PI, (Math.random() - 0.5) * 2)); // 生成点 sizes.push(Math.random() * 1.5 + 0.5); // 添加随机大小 pushShift(); // 添加位置信息 } 

        注释:这部分代码在已有的点基础上,再添加10万个点。使用极坐标系生成点,使其在一定范围内均匀分布,丰富了场景中的点的数量和分布。

9. 创建几何体和材质

        接下来让我们设置一下几何体和其材质样式:

let g = new THREE.BufferGeometry().setFromPoints(pts); // 创建几何体 g.setAttribute("sizes", new THREE.Float32BufferAttribute(sizes, 1)); // 设置sizes属性 g.setAttribute("shift", new THREE.Float32BufferAttribute(shift, 4)); // 设置shift属性 let m = new THREE.PointsMaterial({ // 创建点材质 size: 0.125, // 点的大小 transparent: true, // 透明材质 depthTest: false, // 禁用深度测试 blending: THREE.AdditiveBlending, // 添加混合模式 onBeforeCompile: shader => { // 自定义着色器 shader.uniforms.time = gu.time; // 设置uniform变量 shader.vertexShader = ` uniform float time; attribute float sizes; attribute vec4 shift; varying vec3 vColor; ${shader.vertexShader} `; // 修改顶点着色器 // 更新点的大小、颜色和移动逻辑 shader.vertexShader = shader.vertexShader .replace(`gl_PointSize = size;`, `gl_PointSize = size * sizes;`) .replace(`#include <color_vertex>`, `#include <color_vertex> float d = length(abs(position)/vec3(40.,10.,40)); d=clamp(d,0.,1.); vColor = mix(vec3(227.,155.,0.),vec3(100.,50.,255.),d)/255.;`) .replace(`#include <begin_vertex>`, `#include <begin_vertex> float t = time; float moveT = mod(shift.x + shift.z * t,PI2); float moveS = mod(shift.y + shift.z * t,PI2); transformed += vec3(cos(moveS) * sin(moveT), cos(moveT), sin(moveS) * sin(moveT)) * shift.w;`); // 修改片元着色器 shader.fragmentShader = ` varying vec3 vColor; ${shader.fragmentShader} `.replace(`#include <clipping_planes_fragment>`, `#include <clipping_planes_fragment> float d = length(gl_PointCoord.xy - 0.5);`) .replace(`vec4 diffuseColor = vec4( diffuse, opacity );`, `vec4 diffuseColor = vec4( vColor, smoothstep(0.5, 0.1, d));`); } }); 

        注释:在这部分代码中,创建了一个缓冲几何体并设置了点的大小和移动信息。还定义了一个点材质,使用自定义着色器来控制点的大小、颜色和移动效果,提供了动态的视觉效果。

10. 创建点云并添加到场景

        接下来让我们创建点云并添加到场景:

let p = new THREE.Points(g, m); // 创建点云 p.rotation.order = "ZYX"; // 设置旋转顺序 p.rotation.z = 0.2; // 设置初始旋转角度 scene.add(p); // 将点云添加到场景中 

        注释:这里将创建的几何体和材质结合成一个点云对象,并设置其旋转顺序和初始旋转角度,最后将其添加到场景中以进行渲染。

11. 渲染循环

        最后让我们渲染一下环境:

let clock = new THREE.Clock(); // 创建时钟对象 renderer.setAnimationLoop(() => { // 设置渲染循环 controls.update(); // 更新控制器 let t = clock.getElapsedTime() * 0.5; // 获取流逝时间 gu.time.value = t * Math.PI; // 更新时间 p.rotation.y = t * 0.05; // 更新点云的旋转角度 renderer.render(scene, camera); // 渲染场景和相机 }); 

        注释:最后,这段代码设置了渲染循环,利用时钟对象控制动画的时间,并不断更新控制器和点云的旋转,最终渲染场景。

        通过上述的分层讲解之后,我们就大致的了解每一步都是如何操作并且为什么这么操作的了,为了使读者能更好的理解上述流程,这里我们在进行总结一下:

导入库:使用Three.js库和OrbitControls模块,准备进行三维场景的创建和交互。场景和相机设置:创建一个三维场景并设置背景颜色。创建透视相机,设定相机的位置,准备从特定角度观察场景。渲染器初始化:创建WebGL渲染器,设置其大小与窗口相同,并将其添加到网页中以显示内容。窗口大小自适应:添加事件监听器,以确保在窗口大小变化时,自动调整相机的长宽比和渲染器的大小,保持渲染效果。控制器设置:创建轨道控制器,允许用户通过鼠标控制相机的旋转和缩放,增强用户交互体验。全局变量和点属性初始化:定义用于控制动画的全局变量和点的大小、位移数组,准备生成点的运动效果。点的生成:生成大量随机位置的点,包括中心球体内的点和周围分布的点,以增加视觉复杂度。几何体和材质设置:创建缓冲几何体,设置点的大小和位移信息。定义点的材质,使用自定义着色器来控制点的大小、颜色和移动效果。点云创建和添加到场景:将几何体和材质组合成点云对象,并设置初始旋转,最后将其添加到场景中以进行渲染。渲染循环:使用时钟对象进行动画控制,在每一帧中更新控制器、点云的旋转和动画时间,并渲染场景。

        ——至此,我们就讲解完了上述案例的所以步骤了!!!

——全部代码

        最后在让我们看一下最终的效果,(JavaScript没有极限!!!)

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>秋刀鱼不做梦</title> <style> body { overflow: hidden; margin: 0; } </style> </head> <body> <script type="module"> // 导入三维模型库 import * as THREE from "https://cdn.skypack.dev/[email protected]"; // 导入轨道控制库 import { OrbitControls } from "https://cdn.skypack.dev/[email protected]/examples/jsm/controls/OrbitControls"; // 清除控制台 console.clear() // 创建场景 let scene = new THREE.Scene() // 设置场景背景颜色 scene.background = new THREE.Color(0x160016) // 创建相机 let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000) // 设置相机位置 camera.position.set(0, 4, 21) // 创建渲染器 let renderer = new THREE.WebGLRenderer() // 设置渲染器大小 renderer.setSize(innerWidth, innerHeight) // 把渲染器加入到页面中 document.body.appendChild(renderer.domElement) // 监听窗口大小变化事件 window.addEventListener("resize", event => { camera.aspect = innerWidth / innerHeight camera.updateProjectionMatrix() renderer.setSize(innerWidth, innerHeight) }) // 创建控制器 let controls = new OrbitControls(camera, renderer.domElement) // 开启阻尼效果 controls.enableDamping = true // 禁用面板 controls.enablePan // 创建全局uniform let gu = { time: { value: 0 } } // 创建点的大小数组和移动数组 let sizes = [] let shift = [] // 创建移动函数 let pushShift = () => { shift.push( Math.random() * Math.PI, Math.random() * Math.PI * 2, (Math.random() * 0.9 + 0.1) * Math.PI * 0.1, Math.random() * 0.9 + 0.1 ) } // 创建点的顶点数组(中间的球体) // 创建一个长度为5万的数组pts并y用Array.prototype.map()方法遍历数组并对每个元素进行操作 let pts = new Array(50000).fill().map(p => { // 每次遍历中,会向sizes数组中添加一个随机大小 sizes.push(Math.random() * 1.5 + 0.5) // 调用pushShift()函数添加位置信息,并返回一个随机方向的THREE.Vector对象 pushShift() return new THREE.Vector3().randomDirection().multiplyScalar(Math.random() * 0.5 + 9.5) // }) // 添加更多的点(旁边围绕的) // 先循环生成十万个点 // 每次循环中生成一个随机数rand,再生成一个随机半径radius for (let i = 0; i < 100000; i++) { let r = 10, R = 40; let rand = Math.pow(Math.random(), 1.5); let radius = Math.sqrt(R * R * rand + (1 - rand) * r * r); // 使用new THREE.Vector3().setFromCylindricalCoords()生成一个点。 pts.push(new THREE.Vector3().setFromCylindricalCoords(radius, Math.random() * 2 * Math.PI, (Math.random() - 0.5) * 2)); sizes.push(Math.random() * 1.5 + 0.5); pushShift(); } // 生成一个点g,同时将点的大小和位置信息添加到sizes和shift数组中 let g = new THREE.BufferGeometry().setFromPoints(pts) // 创建了一个缓冲几何体并设置sizes和shift属性 // 注意这里的F要大写Float32BufferAttribute g.setAttribute("sizes", new THREE.Float32BufferAttribute(sizes, 1)) g.setAttribute("shift", new THREE.Float32BufferAttribute(shift, 4)) // 创建点材质 let m = new THREE.PointsMaterial({ // 表示点的大小 size: 0.125, // 设置材质为透明 transparent: true, // 表示禁用深度测试,使点可以叠加 depthTest: false, // 使用假发混合模式 blending: THREE.AdditiveBlending, // 在材质编译之前修改颜色器,在这里,它用来替换顶点着色器和片元着色器,添加uniform // 和attribute,以鸡自定义颜色和移动 onBeforeCompile: shader => { shader.uniforms.time = gu.time // 首先,它为着色器设置了一个uniform变量time,该变量是在点材质中定义的,用来追踪时间 // 然后它定义了两个attribute变量sizes和shift,这两个变量是在缓冲几何体中定义的,用来控制点的大小和移动 // 最后使用replace方法来替换顶点着色器中的代码 shader.vertexShader = ` uniform float time; attribute float sizes; attribute vec4 shift; varying vec3 vColor; ${shader.vertexShader} ` // 注意上面的 ` 不要漏掉了 // 使用replace来替换着色器中的代码 // 更新点的大小 .replace( `gl_PointSize = size;`, `gl_PointSize = size * sizes;` ) // 更新点的颜色 .replace( `#include <color_vertex>`, `#include <color_vertex> float d = length(abs(position)/vec3(40.,10.,40)); d=clamp(d,0.,1.); vColor = mix(vec3(227.,155.,0.),vec3(100.,50.,255.),d)/255.;` ) // 记得加上分号 // 更新点的移动 .replace( `#include <begin_vertex>`, `#include <begin_vertex> float t = time; float moveT = mod(shift.x + shift.z * t,PI2); float moveS = mod(shift.y + shift.z * t,PI2); transformed += vec3(cos(moveS) * sin(moveT),cos(moveT),sin(moveS)*sin(moveT)) * shift.w; ` ) // 修改片元着色器,用来让点的边缘更加圆滑 // 首先,定义一个varying变量vColor,这个变量是在顶点着色器中定义的,用来传递点的颜色到片段着色器 // 然后使用replace方法来替换片段着色器的代码 shader.fragmentShader = ` varying vec3 vColor; ${shader.fragmentShader} `.replace( `#include <clipping_planes_fragment>`, `#include <clipping_planes_fragment> float d = length(gl_PointCoord.xy - 0.5); ` ).replace( // 记得加上空格 `vec4 diffuseColor = vec4( diffuse, opacity );`, `vec4 diffuseColor = vec4( vColor, smoothstep(0.5, 0.1, d)/* * 0.5+0.5*/);` ); } }) // 创建点云并将其添加到场景中,并设置渲染循环 let p = new THREE.Points(g, m) // 旋转顺序为"ZYX" p.rotation.order = "ZYX" // 旋转角度 0.2 p.rotation.z = 0.2 // 把对象(p)添加到场景(scene)中 scene.add(p) // 创建一个时钟对象clock let clock = new THREE.Clock() // 渲染循环,每次循环中会更新控制器,更新p的旋转角度,更新时间 renderer.setAnimationLoop(() => { // 更新控制器 controls.update() // 获取时钟对象(clock)的已经流逝的时间(t)并将他乘0.5 // 先把时钟关了 let t = clock.getElapsedTime() * 0.5 // 将gu.time.value 设置为t*Math.PI gu.time.value = t * Math.PI // 将对象(p)的旋转角度y设置为t*0.05 p.rotation.y = t * 0.05 // 渲染场景(scene)和相机(camera) renderer.render(scene, camera) }) </script> </body> </html>


以上就是本篇文章的全部内容了!!!

Read more

OpenClaw技术深度解析:原理、架构与实战应用

2026年初,OpenClaw开源项目十天获13万GitHub星标,成为AI智能体领域现象级产品,重新定义“AI助手”能力边界,开启从“被动对话”到“主动执行”的范式革命。 1. 引言:从“聊天框”到“工具箱” 传统AI助手(如ChatGPT、Claude)只能“动嘴”给出建议,而OpenClaw的核心创新在于赋予AI“动手能力”——它能够直接操作本地应用、读写文件、执行Shell命令、控制浏览器、发送邮件、管理日程等,真正成为一个长期驻留在设备上的“数字员工”。 这款由奥地利工程师Peter Steinberger发布的开源项目,在中文圈有个更接地气的昵称——“龙虾”。这个可爱的名字来源于项目创始人对甲壳类动物的偏爱,而OpenClaw的图标正是一只活灵活现的龙虾。 2. 核心设计理念 2.1 本地优先(Local-First)架构 OpenClaw采用“本地优先”的设计哲学,所有用户数据(

By Ne0inhk
基于 Rust 与 DeepSeek 大模型的智能 API Mock 生成器构建实录:从环境搭建到架构解析

基于 Rust 与 DeepSeek 大模型的智能 API Mock 生成器构建实录:从环境搭建到架构解析

前言 在现代软件工程中,API 接口的开发与前端联调往往存在时间差。为了解耦前后端开发进度,Mock 数据(模拟数据)的生成显得尤为关键。传统的 Mock 数据生成依赖于静态 JSON 文件或简单的规则引擎,难以覆盖复杂的业务逻辑与语义关联。随着大语言模型(LLM)的兴起,利用 AI 根据 Schema 定义动态生成高保真的模拟数据成为可能。本文详细记录了使用 Rust 语言结合 DeepSeek-V3.2 模型构建智能 Mock 生成器的完整技术路径,涵盖操作系统层面的环境准备、Rust 工具链的深度配置、代码层面的异步架构设计以及编译期的版本兼容性处理。 第一部分:Linux 系统底层的构建环境初始化 Rust 语言的编译与链接过程高度依赖于底层的系统工具链。Rust 编译器 rustc 在生成二进制文件时,需要调用链接器(Linker)将编译后的对象文件(Object Files)与系统库(

By Ne0inhk
【MySQL】数据库的 “红绿灯”:非空、主键、外键到底管什么?

【MySQL】数据库的 “红绿灯”:非空、主键、外键到底管什么?

表的约束:表中一定要有各种约束,通过各种约束,保证未来数据库中的数据的准确的;约束的本质是:通过技术手段倒逼程序员,插入正确的数据,进而保证数据库中的数据的正确的; 一、非空约束 两个值:null(默认的)和not null(不为空) 数据库默认字段基本都是字段为空,但是实际开发时,尽可能保证字段不为空,因为数据为空没办法参与运算。 null Vs ''  null : 表示什么都没有; '' :有,但是为空; 二、default 约束 default : 跟 C++ 的缺省值一样; not null  and default: 注意:如果我们的表中没有设置 default 和 not null 约束,他默认 default

By Ne0inhk
二、Kafka核心架构与分布式存储

二、Kafka核心架构与分布式存储

思维导图 一、Kafka定位与核心特性 Kafka不仅是传统的消息队列中间件,更被官方定义为新一代的分布式事件流平台。它在海量流式计算场景中占据绝对核心地位,具备以下底层物理特性: 高吞吐与高并发:摒弃缓慢的随机寻址,深度依赖操作系统的页缓存与磁盘的顺序追加写。单机即可支撑每秒百万级的高并发数据吞吐。 可靠性与持久化存储:流动的数据直接落盘持久化至日志文件。配合多副本冗余机制,确保物理节点宕机时核心业务数据绝对不丢失。 高可扩展性与解耦:支持零停机数据处理。支持在线动态扩容Broker节点,自动实现海量数据流的负载均衡。极大解耦了微服务系统,提升了全链路数据处理效率。 二、分布式存储基石:HDFS架构深度剖析 要理解现代中间件的数据分布逻辑,必须先解剖大数据存储基石HDFS的底层架构。 HDFS采用中心化控制模型,由主管元数据的NameNode与负责物理存储的DataNode构成。一个超大文件会被物理切分为默认128MB的数据块,分散存储在不同DataNode的磁盘上。 为保障极高的容错率,HDFS制定了基于机架感知的副本放置关键原则。 默认的三副

By Ne0inhk