赞
踩
沉浸式商城主打使用模型展示产品信息,并提供不同展示背景。
由此产生的需求是:
1. 3D模型在网页上展示
2. 切换hdr背景,并调节环境光
接下来介绍实现思路
计划使用three.js来实现3D展示功能。
Threejs是一款WebGL三维引擎,开发文档three.js
在整个项目中新建Base3d.js
然后新建Scene.vue文件,引用3d库,并设置挂载响应式对象
<template>
<div class="scene" id="scene"></div>
</template>
<script setup>
import Base3d from "../utils/Base3d.js";
import { reactive, onMounted } from "vue";
const data = reactive({
base3d: {},
});
onMounted(() => {
data.base3d = new Base3d("#scene");
});
</script>
导入需要的库
// export default Base3d;
import * as THREE from "three";
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
// 导入控制器,轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// 导入模型解析器
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
接着新建一个构造器存放需要的变量,传入selector选择器和一个onFinish函数。
选择器的作用是定位到three.js要渲染的页面,在本项目中传入的是#product
constructor(selector, onFinish) {
this.container = document.querySelector(selector);
this.camera;
this.scene;
this.renderer;
this.model;
this.panzi;
this.animateAction;
this.clock = new THREE.Clock();
this.onFinish = onFinish;
this.init();
this.animate();
this.progressFn;
}
创建好构造器后,进行基本设置初始化。首先是对场景进行初始化,然后初始化相机,因为相机位置的变化才能记录物体的移动
init() {
// 初始化场景
this.initScene();
// 初始化相机
this.initCamera();
// 初始化渲染器
this.initRenderer();
// 控制器
// this.initControls();
// 添加物体
this.addMesh();
}
场景初始化中新建一个场景
initScene() {
this.scene = new THREE.Scene();
}
初始化透视摄像机
initCamera() {
this.camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.25,
200
);
this.camera.position.set(-1.8, 0.6, 2.7);
}
初始化渲染函数,渲染的尺寸大小设置为窗口大小
因为背景图片的hdr中带有不同的光照信息,我们需要在渲染器中设置色调映射
做好渲染设置后,使用appendChild添加所有渲染的元素
initRenderer() {
this.renderer = new THREE.WebGLRenderer({ antialias: true }); //抗锯齿
// 设置屏幕像素比
this.renderer.setPixelRatio(window.devicePixelRatio);
// 渲染的尺寸大小
this.renderer.setSize(window.innerWidth, window.innerHeight);
// 色调映射
this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
this.renderer.toneMappingExposure = 3; //设置曝光值
this.container.appendChild(this.renderer.domElement); //
}
初始化轨道控制器,实现在场景中全景旋转场景的效果
initControls() {
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
}
设置RGBELoader来加载场景hdr纹理,把纹理值赋给场景背景和环境,当点击选择切换场景时,会将场景名称传到setEnvMap中,对应加载hdr
setEnvMap(hdr) {
new RGBELoader().setPath("./files/hdr/").load(hdr + ".hdr", (texture) => {
texture.mapping = THREE.EquirectangularReflectionMapping;
this.scene.background = texture;
this.scene.environment = texture;
});
到了这个阶段,我们已经有了一个设置好的渲染器,但是还没有进行渲染。
接下来在render()中进行渲染。由于网页页面每帧都在刷新,所以需要设置一个动画循环不断地绘制帧画面,保证渲染流畅。
render() {
this.renderer.render(this.scene, this.camera);
}
animate() {
this.renderer.setAnimationLoop(this.render.bind(this));
}
场景初始化了,渲染器也设置了,场景选择也写好了,终于迎来了最基础也是最重要的一步——设置模型
因为模型一般比较大,加载模型需要时间,所以可以设置一个进度来观察加载程度,使用异步函数进行封装。
同时,为了更好地展示产品效果,选择采用播放动画的方式来替代自主操作,这样更能全方位展示产品最好的角度。
setModel(modelName){ return new Promise((resolve,reject) => { const loader = new GLTFLoader().setPath("files/gltf/"); loader.load(modelName,(gltf)=>{ this.model&&this.model.removeFromParent(); this.model = gltf.scene.children[0]; //子元素的第一个就是模型 if('bag2.glb'==modelName&&!this.panzi){ // this.panzi = gltf.scene.children[5]; // this.scene.add(this.panzi) this.scene.add(gltf.scene); // 修改摄像头为模型摄像头 this.camera = gltf.cameras[0] // 调用动画 this.mixer = new THREE.AnimationMixer(gltf.scene.children[1]); //动画混合器,播放动画 this.animateAction = this.mixer.clipAction(gltf.animations[0]); //调度加载动画 // 设置动画播放时长 this.animateAction.setDuration(20).setLoop(THREE.LoopOnce); //设置播放20s,只播放一次 // 设置播放完动画后停止 this.animateAction.clampWhenFinished = true; // 设置灯光 this.spotlight1 = gltf.scene.children[2].children[0]; this.spotlight1.intensity = 1; this.spotlight2 = gltf.scene.children[3].children[0]; this.spotlight2.intensity = 1; this.spotlight3 = gltf.scene.children[4].children[0]; this.spotlight3.intensity = 1; } this.scene.add(this.model); resolve(this.modelName+"模型添加成功"); },(e)=>{ // 模型加载进度 this.onProgress(e); }); }); }
如果一味播放动画,会使得网页交互效果单调。为了增加网页趣味性,通过监听鼠标滚轮事件来对播放的动画进行交互。滚轮的滚动速度决定了动画的播放速度,滚轮暂停,动画也暂停。向前滚轮,动画向前播放,向后滚轮,动画向后播放。
这里的滚动使用到了防抖。
防抖,防抖就是当触发一个事件不会立即执行,会等待 n 秒后再执行该事件,如果在等待 n 秒期间你再次出发,则会重新计时,也就是说防抖不管你触发多少次这个事件,永远只有一次在执行,并且执行的是最后一次
什么意思,针对该项目具体来说,就是我需要滑动一次滚轮的时候,动画播放一段,然后暂停。如果持续滑动滚轮,那动画就持续播放不会暂停。
用防抖的意思来说,就是触发一次滚轮事件的时候,等待n秒(这n秒动画播放),然后执行暂停事件。
onMouseWheel(e) { // console.log(this.animateAction); // 设置播放速度,先监听滚轮方向 let timeScale = e.deltaY > 0 ? 1 : -1; //获取滚轮方向 this.animateAction.setEffectiveTimeScale(timeScale); this.animateAction.paused = false; //去除暂停状态 // 播放动画 this.animateAction.play(); if(this.timeoutid){ clearTimeout(this.timeoutid); } this.timeoutid = setTimeout(()=>{ this.animateAction.halt(0.5); },300) }
动画章节完结~~~
商城
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。