vue2+three.js实现类似VR、3D全景效果
·
vue2+three.js实现类似VR、3D全景效果
效果图:
俩图标是我自己加的前进后退按钮,也是百度了好久,再加上GPT的帮助,才给搞出来。因为需求急,都不看官方文档,百度到一个能跑的demo之后改吧改吧,就先用着了。
下面是代码:
<template>
<div>
<div id="container" ref="container"></div>
</div>
</template>
<script>
import * as THREE from 'three';
import TWEEN from '@tweenjs/tween.js'
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"
export default {
data () {
return {
sceneIndex:0,
door:null,
bigImg: require("../../../assets/555.jpg"), // 图片路径
container: null, // 页面容器
camera: null, // 相机
renderer: null, // 渲染器
scene: null, // 场景
material: null, // 添加材质
texture: null,// 创建纹理贴图
skyBox: null, // 网格
controls: null, // 轨道控制
clock: null, // 轨道更新时间
// 鼠标属性
bMouseDown: false,
x: -1,
y: -1,
isClickCamera: false, // 是否点运动相机
raycaster: null,
mouse: null,
poiObjects:[],
scenesList:[],//场景列表
ImageList:[
'111.jpg',
'222.jpg',
'333.jpg',
'444.jpg',
'555.jpg',
],//场景图片了列表
}
},
mounted () {
this.$nextTick(() => {
this.init();
this.animate();
})
},
created () {
},
methods: {
// 初始化轨道控制
initControls() {
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.controls.target = new THREE.Vector3(0, 0, 0);
this.controls.minDistance = 18; // 相机最近
this.controls.maxDistance = 500; // 相机最远
this.controls.autoRotate = false; // 图片自动旋转
this.controls.enableDamping = false; // 使动画循环使用时阻尼或自转 意思是否有惯性
this.controls.enablePan = false; // 是否开启右键拖拽
this.controls.autoRotateSpeed = 0.5; // 阻尼系数
},
init() {
// 页面容器
this.container = document.getElementById('container');
// 创建渲染器
this.renderer = new THREE.WebGLRenderer();
this.renderer.setPixelRatio(window.devicePixelRatio);
// 设置画布的宽高
this.renderer.setSize(window.innerWidth, window.innerHeight);
// 判断容器中子元素的长度判断当前容器元素中是否已经包含了canvas元素,如果已经有了,则删除原来的canvas元素,添加新的canvas元素;如果没有,则直接添加canvas元素。
let childs = this.container.childNodes;
if (this.container.childNodes.length > 0) {
this.container.removeChild(childs[0]);
this.container.appendChild(this.renderer.domElement);
} else {
this.container.appendChild(this.renderer.domElement);
}
// 创建场景
this.scene = new THREE.Scene();
// 创建相机
this.camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 1, 10000);
this.camera.position.set(5, 0, 0);
this.camera.lookAt(new THREE.Vector3(1, 0, 0)); //让相机指向原点
// 创建轨道控制器
this.initControls();
// 添加材质
this.material = new THREE.MeshBasicMaterial();
// 创建纹理贴图
this.texture = new THREE.TextureLoader().load(this.bigImg);
this.material.map = this.texture;
// 创建网格对象
this.skyBox = new THREE.Mesh(new THREE.SphereBufferGeometry(100, 100, 100), this.material);
this.skyBox.geometry.scale(1, 1, -1);
// 显示坐标光线
// var axisHelper = new THREE.AxisHelper(600); // 显示光线(红色代表X轴,绿色代表Y轴,蓝色代表Z轴)
// 添加到场景中去
// this.scene.add(axisHelper);
this.scene.add(this.skyBox);
// 添加一个箭头
// const arrow1 = new THREE.ArrowHelper(new THREE.Vector3(1, 0, 0), new THREE.Vector3(10, 0, 0), 5, 0xff0000);
// arrow1.name = 'arrow1';
// this.scene.add(arrow1);
// 鼠标事件监听
this.renderer.domElement.addEventListener('pointerdown', this.onMouseDown, false);
this.renderer.domElement.addEventListener('pointerup', this.onMouseUp, false);
this.renderer.domElement.addEventListener('pointermove', this.onMouseMove, false);
// 监听布局变化
window.addEventListener('resize', this.onWindowResize, false);
this.createMoreImage(0)
},
// 更新相机动画
tweenCamera(position, target) {
new TWEEN.Tween(this.camera.position).to({
x: position.x,
y: position.y,
z: position.z
}, 600).easing(TWEEN.Easing.Sinusoidal.InOut).start();
new TWEEN.Tween(this.controls.target).to({
x: target.x,
y: target.y,
z: target.z
}, 600).easing(TWEEN.Easing.Sinusoidal.InOut).start();
},
// 鼠标按下
onMouseDown(event) {
event.preventDefault(); // 取消默认事件
console.log("---onMouseDown---");
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera( mouse, this.camera );
var intersects = raycaster.intersectObjects( this.scene.children,true);
if(intersects.length>0){
console.log(intersects);
let name = intersects[0].object.name
if (!name) {
return
}
if (name == '前进') {
this.sceneIndex +=1;
if (this.sceneIndex > this.ImageList.length - 1) {
this.sceneIndex = this.ImageList.length - 1
return
}
}else if (name == '后退') {
this.sceneIndex--
if (this.sceneIndex < 0) {
this.sceneIndex = 0
}
}
this.createMoreImage(this.sceneIndex)
this.render.render(this.scene,this.camera)
this.camera.position.set(5,0,0)
}
this.isClickCamera = true;
},
// 鼠标放开
onMouseUp(event) {
event.preventDefault(); // 取消默认事件
console.log("---onMouseUp---");
},
// 鼠标移动
onMouseMove(event) {
event.preventDefault(); // 取消默认事件
console.log("---onMouseMove---");
this.isClickCamera = false;
},
onWindowResize() {
// 窗口缩放的时候,保证场景也跟着一起缩放
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
},
animate() {
requestAnimationFrame(this.animate);
this.controls.update(); // 更新轨道控制
TWEEN.update();
this.renderer.render(this.scene, this.camera);
},
// 循环创建场景
createMoreImage(index){
let scene = new THREE.Scene();
scene.name = index
let texture = new THREE.TextureLoader().load(require('../../../assets/' + this.ImageList[index]));
let material = new THREE.MeshBasicMaterial();
material.map = texture;
let skyBox = new THREE.Mesh(new THREE.SphereBufferGeometry(100, 100, 100), material);
skyBox.geometry.scale(1, 1, -1);
scene.add(skyBox)
var hotPoints=[
{
position:{
x:-98,
y:13,
z:10
},
detail:{
"title":"前进"
}
},
{
position:{
x:-58,
y:-37,
z:70
},
detail:{
"title":"后退"
}
}
];
var pointTexture = new THREE.TextureLoader().load(require('../../../assets/6.png'));
var material1 = new THREE.SpriteMaterial( { map: pointTexture,fog: true,color: 0xffffff,} );
for(var i=0;i<hotPoints.length;i++){
var sprite = new THREE.Sprite( material1 );
sprite.scale.set( 10,10,10);
sprite.position.set( hotPoints[i].position.x, hotPoints[i].position.y, hotPoints[i].position.z );
sprite.name = hotPoints[i].detail.title
sprite.detail = hotPoints[i].detail;
sprite.material.depthTest = false;
sprite.renderOrder = i;
sprite.center = new THREE.Vector2(0.5, 0.5); // 设置锚点为精灵对象中心
this.poiObjects.push(sprite);
scene.add( sprite );
}
this.scene = scene
// 播放相机缓慢移动的动画
function animateCamera(targetPos, targetRot, duration) {
var startPos = camera.position.clone();
var startRot = camera.rotation.clone();
var startTime = Date.now();
function animate() {
var now = Date.now();
var timeElapsed = now - startTime;
var progress = timeElapsed / duration;
if (progress >= 1.0) {
camera.position.copy(targetPos);
camera.rotation.copy(targetRot);
return;
}
var easedProgress = Math.sin(progress * Math.PI / 2);
camera.position.lerpVectors(startPos, targetPos, easedProgress);
camera.rotation.slerpQuaternions(startRot, targetRot, easedProgress);
requestAnimationFrame(animate);
}
animate();
}
},
}
}
</script>
这里
这里 tween.js 应该是动画库,切换场景动画,或者视角转动动画等,我好像没用上,
其他没啥好说的,我也不咋会,反正能跑起来,有啥不会问GPT吧。
然会这里面还有一个坑就是,要把body和html的margin和padding变成0 ,不然检测不到点击添加的那俩图标,
他好像是根据点击页面的i像素,来换算成坐标的,有padding或者margin就换算的不准确了,我排查了好久。。。。
实在不行就把调试窗口给单独拎出来
代码有很多用不到的地方和需要优化的地方,我是来不及改了,就先这样吧。
更多推荐
已为社区贡献3条内容
所有评论(0)