前端小练习——星辰宇宙(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 手把手教学:配置飞书机器人 📖 目录 1. 前置准备 2. 创建飞书应用 3. 配置机器人能力 4. 获取必要凭证 5. 配置 OpenClaw 6. 测试机器人 前置准备 在开始之前,请确保你具备以下条件: ✅ 必需条件 * 飞书管理员权限 * 需要创建企业自建应用的权限 * 或联系管理员协助创建 OpenClaw 已安装 # 检查是否已安装 openclaw --version 📋 准备清单 * OpenClaw 已安装并运行 * 有飞书企业管理员权限 * 基本的命令行操作能力 创建飞书应用 步骤 1:进入飞书开放平台 1. 打开浏览器,访问 飞书开放平台 2. 使用��书账号登录 点击右上角 “开发者后台” 步骤 2:创建企业自建应用

低代码AI平台:Coze与Dify深度对比

低代码 AI 平台(如 Coze 和 Dify)旨在降低 AI 应用开发门槛,使开发者甚至非技术人员也能快速构建基于大模型(LLM)的智能应用。它们通常提供可视化编排、插件集成、知识库管理、对话流程设计等功能。在实际项目中,常常需要将这些平台与现有系统集成,或进行二次开发以满足特定业务需求。 以下从 集成方式 与 二次开发能力 两个维度,分别介绍 Coze 和 Dify 的特点及实践建议: 一、Coze(字节跳动) 1. 集成方式 * Webhook / API 调用 Coze 支持通过 Bot ID 和 API Token 调用其提供的 RESTful API,可将 Bot

基于深度学习的纺织品缺陷检测系统演示与介绍(YOLOv12/v11/v8/v5模型+Django+web+训练代码+数据集)

基于深度学习的纺织品缺陷检测系统演示与介绍(YOLOv12/v11/v8/v5模型+Django+web+训练代码+数据集)

视频演示 基于深度学习的纺织品缺陷检测系统 目录 视频演示 1. 前言 2. 项目演示 2.1 用户登录界面 2.2 主界面布局 2.3 个人信息管理 2.4 多模态检测展示 2.5 检测结果保存 2.6 多模型切换 2.7 识别历史浏览 2.8 管理员管理用户信息 2.9 管理员管理识别历史 3.模型训练核心代码 4. 技术栈 5. YOLO模型对比与识别效果解析 5.1 YOLOv5/YOLOv8/YOLOv11/YOLOv12模型对比 5.2 数据集分析

PyWebIO表单进阶之路:从入门到上线只需这6个关键步骤

第一章:PyWebIO 表单快速构建 PyWebIO 是一个轻量级 Python 库,允许开发者无需前端知识即可通过纯 Python 代码构建交互式 Web 界面。特别适用于快速搭建数据采集表单、参数配置页面或简易管理后台,极大提升原型开发效率。 基础表单元素使用 PyWebIO 提供了多种内置函数来创建表单控件,如文本输入、下拉选择、复选框等。所有输入均可通过 input() 系列函数直接获取值。 # 示例:创建包含姓名、年龄和兴趣的表单 from pywebio.input import input, select, checkbox from pywebio.output import put_text name = input("请输入您的姓名:") age = input("请输入您的年龄:"