🍳 小厨暖光 — 项目介绍与技术详解
本文最后更新于55 天前,其中的信息可能已经过时,如有其他问题请留言
AI智能摘要
小厨暖光是一个综合性美食社区平台,旨在为美食爱好者提供一个交流、分享和购物的线上空间。该平台融合了菜谱分享、社交互动、电商交易、会员系统、AI客服、3D视觉体验等功能,采用前后端分离架构,支持多场景下的交互体验。技术栈方面,前端使用Vue.js和Element UI,后端基于Spring Boot,并结合Three.js实现3D视觉效果。此外,项目还集成了智能客服系统、营销工具以及全面的后台管理功能。

集菜谱分享、社交互动、电商交易、会员体系、AI 智能客服、Three.js 3D 视觉体验于一体的全栈美食社区平台。


📑 目录


1. 项目概述

小厨暖光 (Yummly) 是一套面向美食爱好者的全栈社区平台,采用前后端分离架构。项目名称取意”小小厨房里温暖的光”,希望为每一个热爱烹饪的人打造一个分享与交流的线上家园。

核心能力一览

模块功能描述
🍲 菜谱系统发布/浏览/搜索/收藏菜谱,支持封面图、食材清单、分步教程、SKU 多规格
💬 社交互动评论、点赞、收藏、关注、好友私信、美食圈动态 Feed
🛒 电商交易购物车、下单支付、订单流转、配送跟踪、钱包支付
👑 会员体系青铜/白银/黄金/钻石四档,签到、积分、成长值、排行
🎫 营销工具优惠券、秒杀、拼团、积分商城、抽奖转盘
🎨 3D 视觉Three.js 食物粒子动画背景,每页不同场景,鼠标跟随交互
🤖 AI 客服基于 DeepSeek 的智能问答,支持文字对话 + 图片识别 + 菜谱推荐
🖥️ 管理后台用户/菜谱/订单/库存/配送/营销/统计/日志全面管理

项目规模

指标数量
Java 源文件95
Vue/JS 文件130
数据表42 张
API 端点90+
前端页面42
3D 场景类型7 种

2. 技术栈详解

整体架构

 ┌──────────────────────────────────────────────────────────────┐
 │                        浏览器 / 移动端                         │
 ├──────────────────────────────────────────────────────────────┤
 │  前端 (Vue 2 + Element UI + Three.js)                        │
 │  ┌──────────┬──────────┬──────────┬──────────┬───────────┐  │
 │  │ Vue CLI  │  Router  │   Vuex   │  Axios   │ Three.js  │  │
 │  └──────────┴──────────┴──────────┴──────────┴───────────┘  │
 ├──────────────────────────────────────────────────────────────┤
 │  后端 (Spring Boot 2.5.14)                                    │
 │  ┌──────────┬──────────┬──────────┬──────────┬───────────┐  │
 │  │ Security │   JWT    │ MyBatis+ │  Hutool  │  Knife4j  │  │
 │  │  + CORS  │  Token   │   分页   │  FastJSON│  Swagger  │  │
 │  └──────────┴──────────┴──────────┴──────────┴───────────┘  │
 ├──────────────────────────────────────────────────────────────┤
 │  数据层                                                       │
 │  ┌────────────────────┬──────────────────────────────────┐  │
 │  │   MySQL 8.0        │   文件存储 (本地 upload/)         │  │
 │  └────────────────────┴──────────────────────────────────┘  │
 └──────────────────────────────────────────────────────────────┘

前端技术栈

技术版本用途
Vue 22.6.14渐进式前端框架,组件化构建 UI
Vue Router3.5.3SPA 路由管理,含导航守卫实现权限控制
Vuex3.6.2集中式状态管理,管理用户状态、权限、应用配置
Element UI2.15.7企业级 UI 组件库,提供表格、表单、弹窗等 60+ 组件
Axios0.24.0HTTP 客户端,封装请求/响应拦截器处理 JWT 认证
Three.js0.184WebGL 3D 渲染引擎,实现食物粒子动画背景
QRCode1.5.4二维码生成,用于菜谱分享
Vue Quill Editor3.0.6富文本编辑器,菜谱步骤编辑
NProgress0.2.0页面加载进度条
SCSSCSS 预处理器,编写可维护的样式

后端技术栈

技术版本用途
Spring Boot2.5.14Java 企业级后端框架,自动配置 + 起步依赖
Spring Security认证授权框架,过滤器链处理 JWT + CORS
MyBatis Plus3.4.3.4ORM 增强工具,内置分页、代码生成、条件构造器
JWT (jjwt)0.9.1无状态 Token 认证,用户登录状态管理
Knife4j3.0.3Swagger 增强 UI,自动生成 API 文档
Hutool5.7.22Java 工具集,简化文件、加密、日期等操作
FastJSON1.2.83阿里巴巴 JSON 序列化库
MySQL8.0关系型数据库,utf8mb4 字符集

后端模块结构

模块职责
yummly-common公共模块:AjaxResult 统一响应、BaseEntity 基类、全局异常处理、常量定义
yummly-framework框架模块:Spring Security 配置、JWT 过滤器链、MyBatis Plus 分页插件、WebMvc 跨域
yummly-system业务模块:30+ 实体类、20+ Mapper、Service 层、15+ REST 控制器(含管理端)
yummly-admin启动模块:Spring Boot 入口、多环境配置文件、SQL 初始化脚本

部署架构

┌─────────────────────────────────────────┐
│              Docker Compose              │
│                                          │
│  ┌──────────┐  ┌──────────┐  ┌───────┐ │
│  │ Frontend │  │ Backend  │  │ MySQL │ │
│  │ (Nginx)  │  │ (:8088)  │  │ (:3306)│ │
│  │  :80     │  │          │  │       │ │
│  └────┬─────┘  └────┬─────┘  └───┬───┘ │
│       │             │            │      │
│       └─── proxy ───┘            │      │
│                    │─────────────┘      │
└─────────────────────────────────────────┘
  • 前端使用 Nginx Alpine 部署,配置 SPA 路由回退 + API 反向代理
  • 后端使用 多阶段 Docker 构建(Maven 编译 → JRE 运行镜像)
  • 前端通过 vue.config.js devServer proxy 在开发环境代理到后端 8088

3. Three.js 3D 效果实现原理

小厨暖光 的 3D 视觉系统是整个项目前端的一大亮点。它并非简单的第三方组件套用,而是一套完全从零构建的、面向美食主题的程序化 3D 场景系统。

设计理念

这套 3D 系统的核心设计原则是:

  1. 零外部资源依赖 — 所有蔬果模型(苹果、橙子、番茄、南瓜等)均通过 Three.js 原生几何体程序化生成,不依赖任何外部 3D 模型文件(除 plant 场景的 GLB 模型外)
  2. 场景-路由映射 — 7 种不同场景对应不同页面路由,每个页面的 3D 背景各具特色
  3. 性能优先 — 材质缓存、按需渲染、GPU 资源自动回收
  4. 交互自然 — OrbitControls 提供拖拽/缩放/平移,鼠标悬停激活控制

3.1 架构设计

整个 3D 系统封装在一个 Vue 单文件组件 ThreeBackground/index.vue 中,通过 scene prop 切换场景类型:

 ThreeBackground 组件 (Vue SFC)
├── <template>        — DOM 容器 + loading/hint 浮层
├── <script>          — 3D 核心逻辑 (约 1500 行)
│   ├── 场景配置      — SCENE_COLORS 颜色映射
│   ├── 材质缓存      — mat() 工厂函数
│   ├── 模型工厂      — 16 种水果/蔬菜程序化建模
│   ├── 场景构建      — 7 个 buildXxx() 方法
│   ├── 粒子系统      — addParticles() 泛用粒子
│   ├── 动画循环      — requestAnimationFrame + Clock
│   ├── 交互管理      — OrbitControls + 鼠标事件
│   └── 资源清理      — beforeDestroy GPU 回收
└── <style scoped>    — 容器/提示/hint 样式

3.2 场景初始化

场景初始化是整个 3D 系统的入口,在 Vue 的 mounted() 钩子中调用:

// 1. 创建 WebGL 渲染器
this.renderer = new THREE.WebGLRenderer({
  alpha: true,        // 透明背景,与页面 CSS 背景融合
  antialias: true     // 抗锯齿,边缘更平滑
})
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) // 限制像素比 ≤2
this.renderer.shadowMap.enabled = true           // 启用阴影
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap  // 柔和阴影

// 2. 创建场景 + 背景色 + 雾效
this.scene3d = new THREE.Scene()
const sc = SCENE_COLORS[this.scene]  // 根据场景获取配色
this.scene3d.background = new THREE.Color(sc.light)  // 渐变背景
this.scene3d.fog = new THREE.Fog(sc.light, 8, 28)    // 远距离雾化

// 3. 创建透视相机
this.camera = new THREE.PerspectiveCamera(
  50,           // FOV 视场角
  w / h,        // 宽高比
  0.1,          // 近裁剪面
  100           // 远裁剪面
)
this.camera.position.set(0, 1.2, 8.5)  // 相机位置
this.camera.lookAt(0, -0.1, 0)         // 注视目标

关键设计点

  • alpha: true — 渲染器背景透明,3D 场景与页面的 CSS 背景无缝融合
  • Fog 雾效 — 距离 8 单位开始出现,28 单位完全消失,营造空间深度感
  • pixelRatio 限制 — 限制设备像素比不超过 2,在高 DPI 屏幕上避免性能浪费
  • 每页不同配色 — 通过 SCENE_COLORS 映射,7 种场景各有独特的主色和强调色 const SCENE_COLORS = {
    garden: { light: 0xFFF5EE, accent: 0x228B22 }, // 菜园绿
    kitchen: { light: 0xFFF5EE, accent: 0xFF6347 }, // 厨房红
    dessert: { light: 0xFFFACD, accent: 0xFF69B4 }, // 甜点粉
    market: { light: 0xFFFFF0, accent: 0xFFD700 }, // 市集金
    cookbook: { light: 0xFFF8DC, accent: 0xD2691E }, // 菜谱棕
    gathering: { light: 0xFFF5EE, accent: 0x4ECDC4 }, // 聚会青
    error: { light: 0xDDDDDD, accent: 0x999999 }, // 错误灰
    }

3.3 光照系统

一个逼真的 3D 场景离不开精心布置的光照。本系统采用五点光照方案

// 1. 环境光 — 提供基础照明,消除纯黑阴影
this.scene3d.add(new THREE.AmbientLight(sc.light, 0.55))

// 2. 主光源 (Key Light) — 带阴影的方向光
const key = new THREE.DirectionalLight(0xFFF8F0, 0.9)
key.position.set(6, 8, 4)      // 右上方照射
key.castShadow = true
key.shadow.mapSize.set(512, 512)  // 阴影贴图分辨率
key.shadow.camera.near = 0.5
key.shadow.camera.far = 50
key.shadow.camera.left = -10       // 阴影相机视锥
key.shadow.camera.right = 10
key.shadow.camera.top = 10
key.shadow.camera.bottom = -10
key.shadow.bias = -0.0001          // 防止阴影条纹

// 3. 辅助光 (Fill Light) — 暗部补光,用场景强调色
const fill = new THREE.DirectionalLight(sc.accent, 0.35)
fill.position.set(-4, 2, -4)      // 左后方

// 4. 轮廓光 (Rim Light) — 物体边缘高光
const rim = new THREE.DirectionalLight(0xFFFFFF, 0.25)
rim.position.set(0, -1, 3)        // 下方前方

// 5. 半球光 (Hemisphere Light) — 天空-地面渐变光
const hemi = new THREE.HemisphereLight(0xFFFFFF, sc.accent, 0.2)

光照设计遵循三点布光法(Three-point Lighting)的扩展:

  • Key Light 作为主光,携带阴影贴图,赋予场景立体感
  • Fill Light 使用场景强调色,消除主光造成的暗部过黑,同时为暗面注入色彩
  • Rim Light 从下方照射,分离物体与背景
  • Hemisphere Light 模拟天空(白)到地面(accent色)的漫反射过渡

💡 扩展阅读:三点布光是影视和 3D 渲染中最经典的光照技术,源自好莱坞电影摄影。Three.js 通过不同类型的 Light 对象让我们能在 Web 中复现这一技术。

3.4 相机与交互控制

PerspectiveCamera — 透视相机

this.camera = new THREE.PerspectiveCamera(50, w / h, 0.1, 100)
this.camera.position.set(0, 1.2, 8.5)
  • FOV = 50° — 略大于人眼自然视角(约 40-60°),画面不过分夸张
  • Z = 8.5 — 相机距场景中心约 8.5 米,给蔬果模型留有充足空间

OrbitControls — 轨道控制器

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

this.controls = new OrbitControls(this.camera, this.renderer.domElement)
this.controls.enableDamping = true        // 阻尼惯性
this.controls.dampingFactor = 0.12        // 阻尼系数
this.controls.rotateSpeed = 1.2           // 旋转灵敏度
this.controls.zoomSpeed = 1.8             // 缩放灵敏度
this.controls.minDistance = 3.5           // 最近缩放距离
this.controls.maxDistance = 18            // 最远缩放距离
this.controls.maxPolarAngle = Math.PI * 0.68  // 限制俯仰角
this.controls.autoRotate = true           // 自动旋转
this.controls.autoRotateSpeed = 0.25      // 自动旋转速度
this.controls.target.set(0, -0.2, 0)      // 轨道中心
参数作用设计考量
enableDamping鼠标松开后有惯性滑动模拟物理动量,手感更自然
minDistance=3.5限制最近距离防止相机穿透物体
maxDistance=18限制最远距离防止场景消失在雾中
maxPolarAngle限制俯仰角上限防止翻到底部看到空白
autoRotate场景缓慢自转无操作时场景也有动态感

鼠标悬停交互

// 鼠标进入容器 → 启用控制器 + 显示提示
activateControl() {
  this.controls.enabled = true
  this.hintVisible = true
}

// 鼠标离开容器 → 禁用控制器 + 淡出提示
deactivateControl() {
  this.controls.enabled = false
  // 800ms 后开始淡出,再 600ms 后完全隐藏
  setTimeout(() => { this.hintFading = true }, 800)
  setTimeout(() => { this.hintVisible = false }, 1400)
}

这样设计的好处是:用户将鼠标移动到 3D 背景区域时自动激活交互,移开时自动关闭,避免与其他页面元素的滚动/点击产生冲突

3.5 3D 模型构建

这是整个 3D 系统最精彩的部分。所有蔬菜和水果模型完全通过 Three.js 原生几何体组合而成,不依赖任何外部模型文件。

材质缓存策略

const mats = {}
function mat(color, opts = {}) {
  const k = `${color}_${JSON.stringify(opts)}`
  if (!mats[k]) mats[k] = new THREE.MeshStandardMaterial({
    color,
    roughness: 0.35,   // 默认粗糙度
    ...opts
  })
  return mats[k]
}

通过 key 对材质对象进行缓存,相同的 (color, opts) 组合只创建一次材质,多个几何体共享同一材质实例,减少 GPU 内存占用。

基础几何体工具库

Three.js 几何体用途
SphereGeometry番茄、橙子、葡萄粒、樱桃、桃子、蘑菇菌盖
CylinderGeometry果柄、茎干、玉米主体、蜡烛
ConeGeometry胡萝卜主体、花萼叶片
LatheGeometry苹果轮廓、梨轮廓、草莓轮廓、茄子轮廓
TorusGeometry西瓜条纹、南瓜棱纹、面包圈
BoxGeometry砧板、书页、木箱、彩椒棱
TubeGeometry香蕉弯曲体、葡萄茎曲线、樱桃茎曲线
IcosahedronGeometry橙子主体(比球体更有机)
PlaneGeometry地面、花瓣、玉米外壳
CircleGeometry蘑菇菌斑

案例一:苹果 createApple()

苹果的建模展示了 LatheGeometry(旋转成型) 的经典用法:

function createApple(x, y, z, variant = 'red') {
  const pts = []
  // 通过 41 个采样点定义苹果的纵向轮廓曲线
  for (let i = 0; i <= 40; i++) {
    const t = (i / 40) * Math.PI         // 从 0 到 π
    let r = Math.sin(t) * 0.28            // 基础球形轮廓
    if (t < 0.35) r *= 0.65 + 0.35 * (t / 0.35)     // 底部收窄(苹果窝)
    if (t > Math.PI - 0.25) r *= 0.78 + 0.22 * ...  // 顶部收窄(苹果窝)
    pts.push(new THREE.Vector2(r, Math.cos(t) * 0.3 + 0.02))
  }
  // LatheGeometry 将 2D 轮廓绕 Y 轴旋转成 3D 模型
  const body = new THREE.Mesh(
    new THREE.LatheGeometry(pts, 32),  // 32 段旋转细分
    mat(clr, { roughness: 0.25 })      // 低粗糙度模拟光滑果皮
  )
  // ... 添加果柄(CylinderGeometry)+ 小叶子(压扁的 SphereGeometry)
}

LatheGeometry 原理图解

     Y轴
     ↑
     │   ● ← 顶部苹果窝(半径收窄)
     │  ╱ ╲
     │ ●   ● ← 最宽处
     │  ╲ ╱
     │   ● ← 底部苹果窝
     └────────→ X轴(半径)

将上方的 2D 轮廓曲线绕 Y 轴旋转 360°,生成 3D 苹果模型

这种技术的优势在于:

  • 用极少的数据(41 个点)定义出复杂的有机形状
  • 旋转细分(32 段)提供足够的圆滑度
  • 可通过修改轮廓曲线的数学公式产生不同品种

案例二:香蕉 createBanana()

香蕉展示的是 TubeGeometry(管状几何体) 沿 3D 曲线延伸的能力:

// 1. 定义香蕉的 3D 弯曲路径
const curve = new THREE.CatmullRomCurve3([
  new THREE.Vector3(0, -0.55, 0),
  new THREE.Vector3(0.12, -0.2, 0.06),
  new THREE.Vector3(0.18, 0.15, 0.03),
  new THREE.Vector3(0.1, 0.42, -0.04),
  new THREE.Vector3(0, 0.58, 0)
])
// 2. 沿曲线生成管道(24 段细分,半径 0.065,8 边截面)
const tubeGeo = new THREE.TubeGeometry(curve, 24, 0.065, 8, false)
// 3. Z 轴压扁 0.7 倍,模拟香蕉的扁形截面
body.scale.set(1, 1, 0.7)

CatmullRomCurve3 通过 5 个控制点定义了一条光滑的 3D 样条曲线,TubeGeometry 将一个 8 边形截面沿此曲线挤出,天然形成了香蕉的月牙形弯曲。

案例三:葡萄串 createGrapes()

葡萄串展示了组合模式 + 算法化排列

function createGrapes(x, y, z, color = 0x7B2D8B) {
  const rows = [1, 3, 4, 5, 5, 4, 3]  // 每行颗粒数(倒三角排列)
  const grapeRadius = 0.055

  for (let row = 0; row < rows.length; row++) {
    const n = rows[row]
    for (let i = 0; i < n; i++) {
      const offsetX = (i - (n - 1) / 2) * grapeRadius * 2.2  // 水平均匀分布
      const offsetZ = (Math.random() - 0.5) * 0.1              // 前后随机微移
      const grape = new THREE.Mesh(
        new THREE.SphereGeometry(grapeRadius, 8, 8),
        mat(color, { roughness: 0.15 })  // 低粗糙度 = 光滑果皮
      )
      grape.position.set(offsetX, row * grapeRadius * 1.8, offsetZ)
      g.add(grape)
    }
  }
  // 再用 TubeGeometry 添加弯曲的茎
}

通过 rows 数组控制每行颗粒数量,形成自然的倒三角(上宽下窄)葡萄串形。

部分模型工厂一览

函数核心几何体特殊技巧
createAppleLatheGeometry上下收窄模拟苹果窝
createOrangeIcosahedronGeometry二十面体比球体更”有机”
createBananaTubeGeometryCatmullRom 曲线 + Z轴压扁
createPearLatheGeometry分段半径公式(窄→宽→窄)
createWatermelonSphereGeometryTorus 贴球面做条纹
createGrapesSphereGeometry × N倒三角行列排列
createStrawberryLatheGeometry分段半径 + 种子点撒点
createLemonSphereGeometryY轴拉伸 + 两端凸起
createCarrotConeGeometryTorus 环纹 + 顶部叶子簇
createTomatoSphereGeometryY轴压扁 + 星形花萼
createBellPepperSphereGeometry × 3三瓣交叠 + 茎蒂
createBroccoliSphereGeometry × 50半球随机撒点模拟花球
createPumpkinSphereGeometryY轴强烈压扁 + Box 棱纹
createEggplantLatheGeometry下半部剧烈膨胀的轮廓
createMushroomCylinderGeometry + SphereGeometry上半球截断做菌盖
createCornCylinderGeometry行列小球模拟玉米粒
createCherriesSphereGeometry × 2双球 + 双曲线茎
createPeachSphereGeometryY轴拉伸 + Z轴压扁 + 沟缝 + 红晕叠层

3.6 动画系统

动画循环使用经典的 requestAnimationFrame + THREE.Clock 方案:

animate() {
  this.animationId = requestAnimationFrame(this.animate)  // 递归调用
  const t = this.clock.getElapsedTime()  // 自场景创建以来的秒数

  this.objects.forEach(o => {
    // 1. 自转动画
    if (o.rs) o.m.rotation.y += o.rs    // 物体绕 Y 轴缓慢旋转

    // 2. 浮动动画(非粒子物体)
    if (o.fs && !o.isPts) {
      const amp = o.m.userData?.floatAmp || o.fa   // 浮动幅度
      const spd = o.m.userData?.floatSpd || o.fs   // 浮动速度
      o.m.position.y = o.by + Math.sin(t * spd) * amp  // sin 波上下浮动
    }

    // 3. 粒子旋转
    if (o.isPts) o.m.rotation.y += 0.0008
  })

  this.controls.update()                          // 更新阻尼
  this.renderer.render(this.scene3d, this.camera) // 渲染一帧
}

动画对象数据结构

每个加入场景的物体都携带一份动画元数据:

function addObj(ctx, mesh, opts = {}) {
  ctx.objects.push({
    m: mesh,      // Three.js 对象引用
    rs: 0.003,    // rotation speed — 自转速度
    fs: 0.5,      // float speed — 浮动速度
    fa: 0.06,     // float amplitude — 浮动幅度
    by: y,        // base Y — 浮动基准 Y 坐标
    isPts: false  // 是否为粒子系统
  })
}

每个被创建的蔬果模型也内嵌了 userData 用于个性化的动画参数:

g.userData = {
  floatAmp: 0.04 + Math.random() * 0.06,  // 0.04~0.10 随机浮动幅度
  floatSpd: 0.5 + Math.random() * 0.5      // 0.5~1.0  随机浮动速度
}

随机化参数的意义:如果所有物体以相同的速度/幅度浮动,看起来像机械的军队。通过为每个物体赋予略微不同的 floatAmpfloatSpd,产生自然的、去同步化的浮动效果,模拟水中或空气中的漂浮感。

3.7 粒子特效

每个场景都布有漂浮粒子,增加氛围感:

addParticles(color, count, sx, sy, sz) {
  const pos = []
  // 在指定空间范围内随机分布
  for (let i = 0; i < count; i++) {
    pos.push(
      (Math.random() - 0.5) * sx,     // X: ±sx/2
      Math.random() * sy - sy / 2,     // Y: ±sy/2
      (Math.random() - 0.5) * sz      // Z: ±sz/2
    )
  }
  const geo = new THREE.BufferGeometry()
  geo.setAttribute('position', new THREE.Float32BufferAttribute(pos, 3))

  const mat = new THREE.PointsMaterial({
    color,
    size: 0.04,
    transparent: true,
    opacity: 0.55,
    blending: THREE.AdditiveBlending,  // 叠加混合 — 亮色叠加更亮
    depthWrite: false                  // 不写深度缓冲,避免遮挡问题
  })

  const pts = new THREE.Points(geo, mat)
  this.scene3d.add(pts)
  this.objects.push({ m: pts, rs: 0.0015, fs: 0.7, fa: 0.02, by: 0, isPts: true })
}
参数作用
AdditiveBlending叠加混合模式——粒子重叠处变得更亮,模拟光点/萤火虫效果
depthWrite: false不写入深度缓冲,粒子之间不会互相遮挡
transparent + opacity: 0.55半透明,不喧宾夺主

3.8 资源管理与性能优化

3D 渲染是 GPU 密集型操作。当 Vue 组件被销毁(路由切换)时,如果不手动释放 WebGL 资源,会导致显存泄漏——每次进入页面都新增 GPU 内存占用而不释放,最终导致页面卡顿甚至崩溃。

beforeDestroy() {
  window.removeEventListener('resize', this.onResize)
  if (this.controls) this.controls.dispose()  // 销毁控制器

  cancelAnimationFrame(this.animationId)  // 停止动画循环

  // 逐个清理所有场景物体
  this.objects.forEach(obj => {
    const mesh = obj.m || obj.mesh
    if (mesh) {
      mesh.traverse((node) => {
        // 清理几何体(释放 GPU 顶点缓冲)
        if (node.geometry) node.geometry.dispose()
        // 清理材质(释放 GPU 纹理/着色器)
        if (node.material) {
          const mats = Array.isArray(node.material) ? node.material : [node.material]
          mats.forEach(m => {
            Object.keys(m).forEach(key => {
              const val = m[key]
              if (val && val.isTexture) val.dispose() // 清理纹理
            })
            m.dispose()
          })
        }
      })
    }
  })

  // 清理材质缓存
  Object.values(mats).forEach(m => m.dispose())

  // 销毁渲染器
  if (this.renderer) {
    this.renderer.dispose()
    this.renderer.forceContextLoss()  // 强制丢失 WebGL 上下文
  }
}

完整的资源清理链路:

组件销毁
  ├── OrbitControls.dispose()       — 移除事件监听
  ├── cancelAnimationFrame()        — 停止渲染循环
  ├── geometry.dispose() × N        — 释放 GPU 顶点/索引缓冲
  ├── material.dispose() × M        — 释放 GPU 着色器程序
  ├── texture.dispose() × K         — 释放 GPU 纹理内存
  └── renderer.dispose()
      └── .forceContextLoss()       — 释放整个 WebGL 上下文

⚠️ 如果缺少这些清理代码,在 SPA 中频繁切换页面(进入→离开→进入)将导致显存持续增长。这是 Three.js 在单页应用中最常见的性能陷阱

3.9 GLB 模型加载

除了程序化建模外,系统还支持加载 GLB 格式的外部 3D 模型(Plant 场景):

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'

// 1. 配置 DRACO 解压器(大幅减小模型文件体积)
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.7/')
dracoLoader.setDecoderConfig({ type: 'js' })

// 2. 加载 GLB 模型
const loader = new GLTFLoader()
loader.setDRACOLoader(dracoLoader)

loader.load(
  '/models/strelitzia.glb',
  (gltf) => {
    // 3. 自动缩放适配场景
    const box = new THREE.Box3().setFromObject(gltf.scene)
    const size = box.getSize(new THREE.Vector3())
    const maxDim = Math.max(size.x, size.y, size.z)
    gltf.scene.scale.setScalar(3.5 / maxDim)  // 等比缩放到 3.5 单位
    // 4. 居中定位
    gltf.scene.position.set(...)
    this.scene3d.add(gltf.scene)
  },
  (progress) => { /* 加载进度回调 */ },
  (error) => {
    // 5. 加载失败 → 降级到程序化建模
    this.buildFallbackPlant(sc)
  }
)
技术说明
GLB 格式glTF 2.0 二进制格式,Three.js 原生支持,包含几何体+材质+动画
DRACO 压缩Google 开发的 3D 网格压缩算法,可将模型文件缩小 10-25 倍
Box3 自动缩放读取模型的包围盒,自动缩放到适配场景的尺寸
降级策略模型加载失败时自动回退到程序化建模,确保场景始终可用

3.10 Vue 组件集成

在页面中使用 3D 背景非常简单:

<template>
  <div class="home-page">
    <!-- 3D 背景层 -->
    <ThreeBackground scene="garden" color="#FF6B35" />

    <!-- 页面内容层(更高的 z-index) -->
    <div class="page-content">
      <h1>小厨暖光</h1>
      <!-- ... 轮播图、菜谱卡片等 ... -->
    </div>
  </div>
</template>

<script>
import ThreeBackground from '@/components/ThreeBackground'

export default {
  components: { ThreeBackground }
}
</script>

<style scoped>
.home-page {
  position: relative;
  min-height: 100vh;
}
.page-content {
  position: relative;
  z-index: 1;  /* 内容在 3D 背景之上 */
}
</style>

ThreeBackground 组件渲染一个 position: absolute 的全屏 canvas,z-index: 0 确保它位于内容层之下。页面内容只需要设置 z-index: 1 即可覆盖其上。

7 种场景-路由对应关系

场景标识页面3D 内容
garden首页 /胡萝卜、番茄、南瓜、西兰花、玉米 + 菜园地面
kitchen登录 /login、注册 /register砧板上的苹果、橙子、柠檬、番茄、茄子、蘑菇、葡萄
dessert小吃专区 /snacks大篮子盛满水果,散落的苹果、橙子、葡萄
market秒杀 /seckill三个木箱摊位 + 西瓜、南瓜、玉米、番茄
cookbook做法大全 /encyclopedia打开的书本 + 环绕水果 + 蜡烛
gathering美食圈 /community/feed圆形地台 + 环形排列蔬果 + 中心葡萄
plant盆栽展示GLB 模型加载(含 DRACO 解压 + 降级方案)

4. 总结

小厨暖光 展示了如何将 Three.js 深度集成到 Vue 单页应用中:

技术亮点回顾

  1. 零外部模型依赖 — 16 种蔬果模型全部通过 Three.js 原生几何体程序化生成,使用 LatheGeometry 旋转成型、TubeGeometry 曲线挤出、SphereGeometry 组合等纯数学建模技术
  2. 五点光照系统 — 环境光 + 主光(带阴影)+ 辅助光 + 轮廓光 + 半球光,复刻影视级三点布光法
  3. OrbitControls 智能交互 — 鼠标悬停激活/离开停用,避免与页面滚动冲突;阻尼惯性带来自然手感
  4. 去同步浮动动画 — 每个物体有独立的随机浮动参数,Math.sin(t * spd) * amp 产生自然的不规则漂浮感
  5. GPU 资源全生命周期管理 — 组件销毁时完整释放 geometry/material/texture/renderer,杜绝 SPA 显存泄漏
  6. GLB + DRACO 支持 — 外部模型加载 + Google Draco 压缩 + 加载失败降级方案
  7. 材质缓存策略 — 相同颜色和配置的材质只创建一次,多个几何体共享
  8. AdditiveBlending 粒子 — 叠加混合模式产生光点/萤火虫般的半透明粒子氛围
  9. Fog 雾效 + 场景配色 — 每页独立的背景色、强调色和雾效,营造各具特色的视觉氛围

技术栈总结

Vue 2.6 (组件化) + Three.js 0.184 (WebGL 渲染)
    ↕ RESTful API (Axios + JWT)
Spring Boot 2.5 (业务逻辑) + MyBatis Plus 3.4 (数据访问)
    ↕
MySQL 8.0 (持久化) + 本地文件存储 (upload/)
    ↕
Docker Compose (一键部署)

Made with ❤️ by Yummly Team | 小厨暖光,让美食温暖生活

整理资料不易,觉得有帮助可以投喂下博主哦~感谢!
作者:Hueil
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 协议
转载请注明 文章地址 及 作者 哦~
暂无评论

发送评论 编辑评论


                
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇