Vue3+TypeScript项目实战——打造雨雪交加的智慧城市
个人简介
👀个人主页: 前端杂货铺
⚡开源项目: rich-vue3 (基于 Vue3 + TS + Pinia + Element Plus + Spring全家桶 + MySQL)
🙋♂️学习方向: 主攻前端方向,正逐渐往全干发展
📃个人状态: 研发工程师,现效力于中国工业软件事业
🚀人生格言: 积跬步至千里,积小流成江海
🥇推荐学习:🍖开源 rich-vue3 🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js实战 🍒Three.js🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧
内容 | 参考链接 |
---|---|
THREE.JS 专栏 | Three.js 入门 |
文章目录
- 前言
- 项目概况
- 源码路径
- 文件结构与职责
- 写在最后
前言
大家好,这里是前端杂货铺。
这篇文章我们使用 Vue3+TypeScript+Three.js 等主流前端技术,打造 雨雪交加的智慧城市 项目。
three.js-雨雪交加的智慧城市
项目源码 => 请点击此处自行获取 [github] rich-vue3
如果此项目对你有些帮助,欢迎给个免费的 Star !!!(Thanks♪(・ω・)ノ)
项目概况
源码路径
该项目已被托管到 rich-vue3 中,具体源码在 rich-vue3 项目的 rich-vue3-webapp/src/views/city-three 路径。
文件结构与职责
下面是该项目涉及到文件的基本结构:
- base/index.css:页面的基础样式
- config/index.ts:存储项目中需要使用的颜色
- effect/…: 各种效果及特效,包括 天空盒子、扩散半球、扩散圆、旋转四棱锥、飞线、文字、雷达、雨、路径运动、烟雾、雪、建筑物外围线条、透明墙等
- enter/initCity.ts:初始化场景、 创建城市实例、监听浏览器变化、动画
- enter/city.ts:城市类,加载城市模型、初始化各种效果、点击聚焦和滑动滑轮缩放
- utils/index.ts:封装加载城市模型的方法
- index.vue:基本 UI,初始化项目的入口
城市类代码如下,在 initEffect() 方法中会创建很多种效果。
import { loadFBX } from "../utils"
import * as THREE from "three"
import * as TWEEN from "@tweenjs/tween.js"
import { SurroundLine } from "@/views/city-three/effect/surroundLine"
import { Background } from "@/views/city-three/effect/background"
import { Radar } from "../effect/radar"
import { Wall } from "../effect/wall"
import { Circle } from "@/views/city-three/effect/circle"
import { Ball } from "@/views/city-three/effect/ball"
import { Cone } from "@/views/city-three/effect/cone"
import { Fly } from "@/views/city-three/effect/fly"
import { Road } from "@/views/city-three/effect/road"
import { Font } from "@/views/city-three/effect/font"
import { Snow } from "@/views/city-three/effect/snow"
import { Rain } from "@/views/city-three/effect/rain"
import { Smoke } from "@/views/city-three/effect/smoke";export class City {private readonly scene: anyprivate readonly camera: anyprivate readonly controls: anyprivate tweenPosition: anyprivate tweenRotation: anyprivate flag: booleanprivate readonly height: { value: number }private readonly time: { value: number }private readonly top: { value: number }private readonly effect: {snow: anyrain: anysmoke: any}constructor(scene: object, camera: object, controls: any) {this.scene = scenethis.camera = camerathis.controls = controlsthis.flag = falsethis.tweenPosition = nullthis.tweenRotation = nullthis.height = {value: 5}this.time = {value: 0}this.top = {value: 0}// 雪、雨、烟雾this.effect = {snow: null,rain: null,smoke: null}this.loadCity()}loadCity() {// 加载城市模型,并且渲染到画布loadFBX("model/beijing.fbx").then((object: any) => {object.traverse((child: any) => {if (child.isMesh) {new SurroundLine(this.scene, child, this.height, this.time)}})this.initEffect()})}// 初始化效果,各个功能点都放在了这里initEffect() {new Background(this.scene)new Radar(this.scene, this.time)new Wall(this.scene, this.time)new Circle(this.scene, this.time)new Ball(this.scene, this.time)new Cone(this.scene, this.top, this.height)new Fly(this.scene, this.time)new Road(this.scene, this.time)new Font(this.scene)this.effect.snow = new Snow(this.scene)this.effect.rain = new Rain(this.scene)this.effect.smoke = new Smoke(this.scene)// 点击选择this.addClick()this.addWheel()}addClick() {let flag = truedocument.onmousedown = () => {flag = truedocument.onmousemove = () => {flag = false}}document.onmouseup = (event) => {if (flag) {this.clickEvent(event)}document.onmousemove = null}}// 场景跟随鼠标坐标缩放addWheel() {const body: HTMLElement = document.body// @ts-ignorebody.onmousewheel = (event: MouseEvent) => {// 鼠标当前的坐标const x = (event.clientX / window.innerWidth) * 2 - 1const y = -(event.clientY / window.innerHeight) * 2 + 1const value = 30const vector = new THREE.Vector3(x, y, 0.5)vector.unproject(this.camera)vector.sub(this.camera.position).normalize()// @ts-ignoreif (event.wheelDelta > 0) {this.camera.position.x += vector.x * valuethis.camera.position.y += vector.y * valuethis.camera.position.z += vector.z * valuethis.controls.target.x += vector.x * valuethis.controls.target.y += vector.y * valuethis.controls.target.z += vector.z * value} else {this.camera.position.x -= vector.x * valuethis.camera.position.y -= vector.y * valuethis.camera.position.z -= vector.z * valuethis.controls.target.x -= vector.x * valuethis.controls.target.y -= vector.y * valuethis.controls.target.z -= vector.z * value}}}// 点击聚焦clickEvent(event: MouseEvent) {// 归一化坐标(将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1))const x = (event.clientX / window.innerWidth) * 2 - 1const y = -(event.clientY / window.innerHeight) * 2 + 1// 创建设备坐标(三维)const standardVector = new THREE.Vector3(x, y, 0.5)// 转化为世界坐标 (将此向量 (坐标) 从相机的标准化设备坐标 (NDC) 空间投影到世界空间)const worldVector = standardVector.unproject(this.camera)// 做序列化const ray = worldVector.sub(this.camera.position).normalize()// 实现点击选中// 创建一个射线发射器,用来发射一条射线const raycaster = new THREE.Raycaster(this.camera.position, ray)// 返回射线碰撞到的物体const intersects = raycaster.intersectObjects(this.scene.children, true)let point3d = nullif (intersects.length) {point3d = intersects[0]}if (point3d) {const proportion = 3// 开始动画修改观察点const time = 1000this.tweenPosition = new TWEEN.Tween(this.camera.position).to({ x: point3d.point.x * proportion, y: point3d.point.y * proportion, z: point3d.point.y * proportion }, time).start()this.tweenRotation = new TWEEN.Tween(this.camera.rotation).to({ x: this.camera.rotation.x, y: this.camera.rotation.y, z: this.camera.rotation.z }, time).start()}}start(delta: number) {for (const key in this.effect) {// @ts-ignorethis.effect[key] && this.effect[key].animation()}if (this.tweenPosition && this.tweenRotation) {this.tweenPosition.update()this.tweenRotation.update()}this.height.value += 0.4if (this.height.value > 160) {this.height.value = 5}this.time.value += deltaif (this.top.value > 15 || this.top.value < 0) {this.flag = !this.flag}this.top.value += this.flag ? -0.8 : 0.8}
}
写在最后
由于本项目涉及到的代码较多,在本篇文章中就不一一讲解了,感兴趣的同学可以去下载项目源码自行学习,有问题的话可以评论区一起讨论交流~
好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!
参考资料:
- Three.js 官方文档
- WebGL+Three.js 入门与实战【作者:慕课网_yancy】
相关文章:

Vue3+TypeScript项目实战——打造雨雪交加的智慧城市
个人简介 👀个人主页: 前端杂货铺 ⚡开源项目: rich-vue3 (基于 Vue3 TS Pinia Element Plus Spring全家桶 MySQL) 🙋♂️学习方向: 主攻前端方向,正逐渐往全干发展 …...

经典游戏案例:植物大战僵尸
学习目标:植物大战僵尸核心玩法实现 游戏画面 项目结构目录 部分核心代码 using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using Random UnityEngine.Random;public enum Z…...

Go 与 Java 字符编码选择:UTF-8 与 UTF-16 的较量
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…...

vscode+picgo+gitee实现Markdown图床
vscode中编辑Markdown文件,复制的图片默认是保存在本地的。当文档上传csdn时,会提示图片无法识别 可以在gitee上创建图床仓库,使用picgo工具上传图片,在Markdown中插入gitee链接的方式来解决该问题。 一、 安装picgo工具 1.1 v…...
【thinkphp问题栏】tp5.0分页技巧
一、调用内置方法paginate thinkphp内置了一个paginate方法支持分页功能 该方法位于library\think\db\Query.php内 /*** 分页查询* param int|array $listRows 每页数量 数组表示配置参数* param int|bool $simple 是否简洁模式或者总记录数* param array $config 配…...

获取时间戳是使用System.currentTimeMillis()还是使用new Date().getTime()(阿里开发规范)?
1.阿里规范 在阿里的Java开发手册中强制要求使用System.currentTimeMillis() 2.为什么(源码详解) new Date().getTime()它实际上也是调用的System.currentTimeMillis(),源码分析。 这个fastTime是它的成员变量,在new Date()的时候就被赋值了。 扩展一…...

仿饿了么加入购物车旋转控件 - 自带闪转腾挪动画 的按钮
, mWidth - mCircleWidth, mHeight - mCircleWidth); canvas.drawRoundRect(rectF, mHintBgRoundValue, mHintBgRoundValue, mHintPaint); //前景文字 mHintPaint.setColor(mHintFgColor); // 计算Baseline绘制的起点X轴坐标 int baseX (int) (mWidth / 2 - mHintPaint.m…...
Docker部署nacos集群
docker拉取nacos镜像,本文使用nacos2.0.3 三台服务器都要执行以下命令 docker pull nacos/nacos-server:v2.2.0准备挂载的日志目录和配置文件目录 日志:mkdir /usr/local/software/nacos/logs 配置文件:/usr/local/software/nacos/conf在配…...
centos查找文件 及 操作写入的进程
du -sh * 查看目录空间占用、发现大文件,确定进程,结束 yum install lsof 安装lsof 查看文件写入的 进程 2. lsof /root/.influxdbv2/engine/data/bab49411e5f7cbce/autogen/1/000000036-000000002.tsm COMMAND PID USER FD TYPE …...
构建高可用Java微服务架构的秘籍
构建高可用Java微服务架构的秘籍 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 随着云计算和分布式系统的快速发展,微服务架构已成为构建大型应用…...

VBA学习(18):VBA制作任意工作表均可使用的聚光灯
在需要制作聚光的工作簿,按<ALTF11>组合键,打开VBE编辑器。在右侧[工程资源管理器窗格]选中ThisWorkbook模块,将以下代码复制粘贴到该模块的代码窗口。 Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target …...

【STM32-启动文件 startup_stm32f103xe.s】
STM32-启动文件 startup_stm32f103xe.s ■ STM32-启动文件■ STM32-启动文件主要做了以下工作:■ STM32-启动文件指令■ STM32-启动文件代码详解■ 栈空间的开辟■ 栈空间大小 Stack_Size■ .map 文件的详细介绍■ 打开map文件 ■ 堆空间■ PRESERVE8 和 THUMB 指令…...

51学习记录(一)——51介绍及震动感应灯
文章目录 前言一、STC89C522.内部结构及引脚 二、继电器原理及震动传感器原理三、项目搭建及实现 前言 一个学习嵌入式的小白~ 有问题评论区或私信指出 提示:以下是本篇文章正文内容,下面案例可供参考 一、STC89C52 1.简介 所属系列:51单…...

2024GLEE生活暨教育(上海)博览会,8月20-22日,国家会展中心(上海)
2024GLEE生活暨教育(上海)博览会将于8月20-22日在中国国家会展中心(上海)举行,博览会总面积近万平方米,设有美好生活和教育产品两大主力展区,全面覆盖婴幼儿、学龄前、小学、初中、高中、大学、中年、老年各个年龄段的…...

debug调试高级功能 断点、布局 及Android Studio常用快捷按键使用详情
文章目录 debug断点篇:打临时断点(只用一次):alt断点条件断点:在断点上,点击右键,在Condition那里,设置我们需要的值,循环就会自动停到我们设置的那个值那里依赖断点&…...

51单片机STC89C52RC——6.1 中断系统
一,文字层面理解 反正我看下面的几段文字时脑壳没有正常运转。一个头几个大 中断系统是为使CPU具有对外界紧急事件的实时处理能力而设置的。 当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这…...
Redis源码学习:高性能Hash表的设计与实现
哈希表(Hash)是Redis数据库的数据类型之一,理解哈希表的实现对于掌握Redis非常重要。这篇文章,从哈希冲突和哈希扩展这两个角度,来一步步讲解Redis哈希表的工作原理。 什么是哈希表? 哈希表是一种通过哈希…...
如何防范常见的数据库安全问题
随着数据量的增加和系统的复杂性提高,数据库可能面临多种安全威胁,包括未授权访问、数据泄露、注入攻击等。 1. 未授权访问 未授权访问是指,未经授权的用户对数据库的内容进行访问。这会导致数据泄露、数据篡改或其他安全事故。 针对未授权访问的防范措施如下。 (1)强化…...
[Day 19] 區塊鏈與人工智能的聯動應用:理論、技術與實踐
區塊鏈的數據透明性 區塊鏈技術作為一種分布式賬本技術,因其去中心化、不可篡改和高度透明的特性,已經在各行各業中得到了廣泛應用。在本文中,我們將深入探討區塊鏈的數據透明性,包括其原理、實現方法及相關代碼示例,…...

【Hadoop学习笔记】认识Hadoop
认识Hadoop 从网上找的课程做的笔记,有些图是自己理解画的,可能不正确,可以作为参考,有疑问的地方请直接指出,共同交流。 Hadoop是由Apache基金会开发的一个分布式系统基础架构,主要解决海量数据的存储和海…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...

超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...