|
@@ -1,11 +1,19 @@
|
|
|
<template>
|
|
|
- <div class="three-model" @click="clickEvent">
|
|
|
+ <div class="three-model" :style="'cursor: ' + (pointer ? 'pointer' : 'auto') + ';'" @click="clickEvent">
|
|
|
<loading ref="pageLoading"></loading>
|
|
|
+ <div class="three-html-layer">
|
|
|
+ <div
|
|
|
+ class="three-html-dom fan-info"
|
|
|
+ :id="htmlLayer[0].id"
|
|
|
+ v-show="htmlLayer[0].show"
|
|
|
+ :style="'left: ' + htmlLayer[0].x + 'px; top: ' + htmlLayer[0].y + 'px;'"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import loading from '@com/coms/loading/loading.vue';
|
|
|
+import loading from "@com/coms/loading/loading.vue";
|
|
|
import * as THREE from "three";
|
|
|
import { GLTFLoader } from "@node/three/examples/jsm/loaders/GLTFLoader.js";
|
|
|
import { OrbitControls } from "@node/three/examples/jsm/controls/OrbitControls.js";
|
|
@@ -18,7 +26,7 @@ export default {
|
|
|
name: "ThreeModel",
|
|
|
// 使用组件
|
|
|
components: {
|
|
|
- loading
|
|
|
+ loading,
|
|
|
},
|
|
|
// 传入参数
|
|
|
props: {},
|
|
@@ -29,169 +37,267 @@ export default {
|
|
|
// 数据
|
|
|
data() {
|
|
|
return {
|
|
|
- // 场景
|
|
|
- initScene: function () {
|
|
|
- scene = new THREE.Scene();
|
|
|
- // scene.background = new THREE.Color(0xa0a0a0);
|
|
|
- },
|
|
|
- // 相机
|
|
|
- initCamera: function () {
|
|
|
- camera = new THREE.PerspectiveCamera(45, this.$el.scrollWidth / this.$el.scrollHeight, 0.1, 10000);
|
|
|
- camera.position.set(50, 60, 50);
|
|
|
- },
|
|
|
- // 坐标轴
|
|
|
- initAxesHelper: function () {
|
|
|
- const axesHelper = new THREE.AxesHelper(150);
|
|
|
- scene.add(axesHelper);
|
|
|
- axesHelper.position.set(0, 0, 0);
|
|
|
- },
|
|
|
- // 渲染器
|
|
|
- initRender: function () {
|
|
|
- renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
|
|
|
- // renderer.setClearAlpha(0);
|
|
|
- renderer.setSize(this.$el.scrollWidth, this.$el.scrollHeight);
|
|
|
- this.$el.append(renderer.domElement);
|
|
|
- },
|
|
|
- // 灯光
|
|
|
- initLight: function () {
|
|
|
- // let light = new THREE.PointLight(0xffffff, 2);
|
|
|
- let light = new THREE.AmbientLight(0xffffff, 4);
|
|
|
- // light.position.set(50, 50, 50);
|
|
|
- scene.add(light);
|
|
|
- },
|
|
|
- // 控制器
|
|
|
- initControls: function () {
|
|
|
- controls = new OrbitControls(camera, renderer.domElement);
|
|
|
- },
|
|
|
- // 初始化一个风机动画
|
|
|
- initFanAnimate: function (fan1, fan2, fan3) {
|
|
|
- let fanAnimateObj = {
|
|
|
- speed: 0.05,
|
|
|
- fan1: fan1,
|
|
|
- fan2: fan2,
|
|
|
- fan3: fan3,
|
|
|
- };
|
|
|
- let fanAnimateFunction = function () {
|
|
|
- fanAnimateObj.fan1.rotateZ(fanAnimateObj.speed);
|
|
|
- fanAnimateObj.fan2.rotateZ(fanAnimateObj.speed);
|
|
|
- fanAnimateObj.fan3.rotateZ(fanAnimateObj.speed);
|
|
|
- fanAnimateObj.animateId = window.requestAnimationFrame(fanAnimateFunction);
|
|
|
- }
|
|
|
- fanAnimateObj.stop = function () {
|
|
|
- window.cancelAnimationFrame(fanAnimateObj.animateId);
|
|
|
- };
|
|
|
- fanAnimateObj.start = function () {
|
|
|
- fanAnimateFunction();
|
|
|
- };
|
|
|
+ pointer: false,
|
|
|
+ htmlLayer: [
|
|
|
+ {
|
|
|
+ id: "fan-info",
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ show: false,
|
|
|
+ ox: 50,
|
|
|
+ oy: -50,
|
|
|
+ position: null,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ };
|
|
|
+ },
|
|
|
+ // 函数
|
|
|
+ methods: {
|
|
|
+ // Vector3 to screen
|
|
|
+ vector3ToScreen: function(position) {
|
|
|
+ const centerX = this.$el.scrollWidth / 2;
|
|
|
+ const centerY = this.$el.scrollHeight / 2;
|
|
|
+ const v3 = new THREE.Vector3(position.x, position.y, position.z);
|
|
|
+ const standardVec = v3.project(camera);
|
|
|
+ const screenX = Math.round(centerX * standardVec.x + centerX);
|
|
|
+ const screenY = Math.round(-centerY * standardVec.y + centerY);
|
|
|
+ return { x: screenX, y: screenY };
|
|
|
+ },
|
|
|
+ // 场景
|
|
|
+ initScene: function() {
|
|
|
+ scene = new THREE.Scene();
|
|
|
+ // scene.background = new THREE.Color(0xa0a0a0);
|
|
|
+ },
|
|
|
+ // 相机
|
|
|
+ initCamera: function() {
|
|
|
+ camera = new THREE.PerspectiveCamera(45, this.$el.scrollWidth / this.$el.scrollHeight, 0.1, 10000);
|
|
|
+ camera.position.set(50, 60, 50);
|
|
|
+ },
|
|
|
+ // 坐标轴
|
|
|
+ initAxesHelper: function() {
|
|
|
+ const axesHelper = new THREE.AxesHelper(150);
|
|
|
+ scene.add(axesHelper);
|
|
|
+ axesHelper.position.set(0, 0, 0);
|
|
|
+ },
|
|
|
+ // 渲染器
|
|
|
+ initRender: function() {
|
|
|
+ renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
|
|
+ // renderer.setClearAlpha(0);
|
|
|
+ renderer.setSize(this.$el.scrollWidth, this.$el.scrollHeight);
|
|
|
+ this.$el.append(renderer.domElement);
|
|
|
+ },
|
|
|
+ // 灯光
|
|
|
+ initLight: function() {
|
|
|
+ // let light = new THREE.PointLight(0xffffff, 2);
|
|
|
+ let light = new THREE.AmbientLight(0xffffff, 4);
|
|
|
+ // light.position.set(50, 50, 50);
|
|
|
+ scene.add(light);
|
|
|
+ },
|
|
|
+ // 控制器
|
|
|
+ initControls: function() {
|
|
|
+ controls = new OrbitControls(camera, renderer.domElement);
|
|
|
+ controls.addEventListener("change", () => {
|
|
|
+ this.setEveryHTML();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 初始化一个风机动画
|
|
|
+ initFanAnimate: function(fan1, fan2, fan3) {
|
|
|
+ let fanAnimateObj = {
|
|
|
+ speed: 0.05,
|
|
|
+ fan1: fan1,
|
|
|
+ fan2: fan2,
|
|
|
+ fan3: fan3,
|
|
|
+ };
|
|
|
+ let fanAnimateFunction = function() {
|
|
|
+ fanAnimateObj.fan1.rotateZ(fanAnimateObj.speed);
|
|
|
+ fanAnimateObj.fan2.rotateZ(fanAnimateObj.speed);
|
|
|
+ fanAnimateObj.fan3.rotateZ(fanAnimateObj.speed);
|
|
|
+ fanAnimateObj.animateId = window.requestAnimationFrame(fanAnimateFunction);
|
|
|
+ };
|
|
|
+ fanAnimateObj.stop = function() {
|
|
|
+ window.cancelAnimationFrame(fanAnimateObj.animateId);
|
|
|
+ };
|
|
|
+ fanAnimateObj.start = function() {
|
|
|
fanAnimateFunction();
|
|
|
- fanAnimates.push(fanAnimateObj);
|
|
|
- return fanAnimateObj;
|
|
|
- },
|
|
|
- // 清空风机动画
|
|
|
- clearFanAnimate: function () {
|
|
|
- while (fanAnimates.length > 0) {
|
|
|
- let fanAnimateObj = fanAnimates.shift();
|
|
|
- fanAnimateObj.stop();
|
|
|
- }
|
|
|
- },
|
|
|
- // 内容
|
|
|
- initContent: function () {
|
|
|
- // 加载3D地面
|
|
|
- let loaderGround = new GLTFLoader();/*实例化加载器*/
|
|
|
- loaderGround.load("static/3d/group/ng.gltf", (gltf) => {
|
|
|
+ };
|
|
|
+ fanAnimateFunction();
|
|
|
+ fanAnimates.push(fanAnimateObj);
|
|
|
+ return fanAnimateObj;
|
|
|
+ },
|
|
|
+ // 清空风机动画
|
|
|
+ clearFanAnimate: function() {
|
|
|
+ while (fanAnimates.length > 0) {
|
|
|
+ let fanAnimateObj = fanAnimates.shift();
|
|
|
+ fanAnimateObj.stop();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 内容
|
|
|
+ initContent: function() {
|
|
|
+ // 加载3D地面
|
|
|
+ let loaderGround = new GLTFLoader(); /*实例化加载器*/
|
|
|
+ loaderGround.load(
|
|
|
+ "static/3d/group/ng.gltf",
|
|
|
+ (gltf) => {
|
|
|
gltf.scene.position.set(5, 10, 0);
|
|
|
scene.add(gltf.scene);
|
|
|
// 找到一个大风扇的 Object3D
|
|
|
let fanBig1_1 = gltf.scene.children[0].getObjectByName("Box707");
|
|
|
let fanBig1_2 = gltf.scene.children[0].getObjectByName("Box708");
|
|
|
- let fanBig1_3 = gltf.scene.children[0].getObjectByName("Box709");
|
|
|
+ let fanBig1_3 = gltf.scene.children[0].getObjectByName("Box709"); // 组840
|
|
|
let fanBig2_1 = gltf.scene.children[0].getObjectByName("Box719");
|
|
|
let fanBig2_2 = gltf.scene.children[0].getObjectByName("Box720");
|
|
|
- let fanBig2_3 = gltf.scene.children[0].getObjectByName("Box721");
|
|
|
+ let fanBig2_3 = gltf.scene.children[0].getObjectByName("Box721"); // 组857
|
|
|
let fanBig3_1 = gltf.scene.children[0].getObjectByName("Box699");
|
|
|
let fanBig3_2 = gltf.scene.children[0].getObjectByName("Box701");
|
|
|
- let fanBig3_3 = gltf.scene.children[0].getObjectByName("Box702");
|
|
|
+ let fanBig3_3 = gltf.scene.children[0].getObjectByName("Box702"); // 组838
|
|
|
this.initFanAnimate(fanBig1_1, fanBig1_2, fanBig1_3);
|
|
|
this.initFanAnimate(fanBig2_1, fanBig2_2, fanBig2_3);
|
|
|
this.initFanAnimate(fanBig3_1, fanBig3_2, fanBig3_3);
|
|
|
- let mixer = new THREE.AnimationMixer(gltf.scene.children[0]);
|
|
|
- mixer.clipAction(gltf.animations[0]).setDuration(3).play();
|
|
|
+ let mixer = new THREE.AnimationMixer(gltf.scene.children[0]);
|
|
|
+ mixer
|
|
|
+ .clipAction(gltf.animations[0])
|
|
|
+ .setDuration(3)
|
|
|
+ .play();
|
|
|
mixers.push(mixer);
|
|
|
- console.log(gltf)
|
|
|
- }, (xhr) => {
|
|
|
+ console.log(gltf);
|
|
|
+ },
|
|
|
+ (xhr) => {
|
|
|
if (xhr.loaded == xhr.total) {
|
|
|
this.$emit("when");
|
|
|
setTimeout(() => {
|
|
|
this.$refs.pageLoading.hide();
|
|
|
}, 3000);
|
|
|
}
|
|
|
- }, function (error) {
|
|
|
- console.error('load error!'+error.getWebGLErrorMessage());
|
|
|
- })
|
|
|
- },
|
|
|
- // 创建一个圆柱
|
|
|
- initCylinderGeometry: function () {
|
|
|
- let geometry = new THREE.CylinderGeometry(4, 4, 4, 64);
|
|
|
- let materials = [
|
|
|
+ },
|
|
|
+ function(error) {
|
|
|
+ console.error("load error!" + error.getWebGLErrorMessage());
|
|
|
+ }
|
|
|
+ );
|
|
|
+ },
|
|
|
+ // 创建一个圆柱
|
|
|
+ initCylinderGeometry: function(position) {
|
|
|
+ let geometry = new THREE.CylinderGeometry(2, 2, 7, 64);
|
|
|
+ //加载纹理
|
|
|
+ let texture = new THREE.TextureLoader().load("static/3d/beam-texture.png");
|
|
|
+ texture.wrapS = texture.wrapT = THREE.RepeatWrapping; //每个都重复
|
|
|
+ texture.repeat.set(1, 1);
|
|
|
+ texture.needsUpdate = true;
|
|
|
+ let materials = [
|
|
|
//圆柱侧面材质,使用纹理贴图
|
|
|
new THREE.MeshBasicMaterial({
|
|
|
- color: 0xffff00,
|
|
|
+ map: texture,
|
|
|
side: THREE.DoubleSide,
|
|
|
- transparent: true
|
|
|
+ transparent: true,
|
|
|
}),
|
|
|
//圆柱顶材质
|
|
|
new THREE.MeshBasicMaterial({
|
|
|
transparent: true,
|
|
|
opacity: 0,
|
|
|
- side: THREE.DoubleSide
|
|
|
+ side: THREE.DoubleSide,
|
|
|
}),
|
|
|
//圆柱底材质
|
|
|
new THREE.MeshBasicMaterial({
|
|
|
transparent: true,
|
|
|
opacity: 0,
|
|
|
- side: THREE.DoubleSide
|
|
|
- })
|
|
|
- ];
|
|
|
- let cylinder = new THREE.Mesh(geometry, materials);
|
|
|
- scene.add(cylinder);
|
|
|
- },
|
|
|
- // 点击事件
|
|
|
- clickEvent: function (event) {
|
|
|
- event.preventDefault();
|
|
|
- let vector = new THREE.Vector3(
|
|
|
- (event.clientX / window.innerWidth) * 2 - 1,
|
|
|
- -(event.clientY / window.innerHeight) * 2 + 1,
|
|
|
- 0.5
|
|
|
- );
|
|
|
- vector = vector.unproject(camera);
|
|
|
- let raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
|
|
|
- let intersects = raycaster.intersectObjects(scene.children,true);
|
|
|
- console.log(intersects)
|
|
|
- // this.initCylinderGeometry();
|
|
|
- },
|
|
|
- // 初始化
|
|
|
- initThree: function () {
|
|
|
- this.initScene();
|
|
|
- // this.initAxesHelper();
|
|
|
- this.initCamera();
|
|
|
- this.initRender();
|
|
|
- this.initLight();
|
|
|
- this.initControls();
|
|
|
- this.initContent();
|
|
|
- renderer.setAnimationLoop(this.animate);
|
|
|
- },
|
|
|
- // 动画
|
|
|
- animate: function () {
|
|
|
- var delta = clock.getDelta();
|
|
|
- for ( var i = 0; i < mixers.length; i ++ ) { // 重复播放动画
|
|
|
- mixers[ i ].update( delta );
|
|
|
+ side: THREE.DoubleSide,
|
|
|
+ }),
|
|
|
+ ];
|
|
|
+ let cylinder = new THREE.Mesh(geometry, materials);
|
|
|
+ cylinder.position.set(position.x + 5.75, position.y + 8, position.z);
|
|
|
+ scene.add(cylinder);
|
|
|
+ },
|
|
|
+ // 判断时候是可点击对象 返回null 或object
|
|
|
+ getClickObject: function(intersects) {
|
|
|
+ const names = ["组840", "组857", "组838"];
|
|
|
+ const x = true;
|
|
|
+ let obj = null;
|
|
|
+ for (let intersect of intersects) {
|
|
|
+ let temObj = intersect.object;
|
|
|
+ while (x) {
|
|
|
+ if (names.indexOf(temObj.name) >= 0) {
|
|
|
+ obj = temObj;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (temObj.parent) {
|
|
|
+ temObj = temObj.parent;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ break;
|
|
|
}
|
|
|
- renderer.render(scene, camera);
|
|
|
- },
|
|
|
- };
|
|
|
+ }
|
|
|
+ return obj;
|
|
|
+ },
|
|
|
+ // 鼠标浮动事件 // 这招不太行 鼠标每次移动都会发射射线 导致动画卡顿
|
|
|
+ mοusemοveEvent: function(event) {
|
|
|
+ event.preventDefault();
|
|
|
+ let vector = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1, 0.5);
|
|
|
+ vector = vector.unproject(camera);
|
|
|
+ let raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
|
|
|
+ let intersects = raycaster.intersectObjects(scene.children, true);
|
|
|
+ let obj = this.getClickObject(intersects);
|
|
|
+ if (obj) {
|
|
|
+ this.pointer = true;
|
|
|
+ } else {
|
|
|
+ this.pointer = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 设置每一个html的位置
|
|
|
+ setEveryHTML: function() {
|
|
|
+ this.htmlLayer.forEach(value => {
|
|
|
+ if (value.show && value.position) {
|
|
|
+ const screen = this.vector3ToScreen(value.position);
|
|
|
+ value.x = screen.x + value.ox;
|
|
|
+ value.y = screen.y + value.oy;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 显示风机弹出层
|
|
|
+ showFanInfo: function(position) {
|
|
|
+ this.htmlLayer[0].position = {
|
|
|
+ x: position.x,
|
|
|
+ y: position.y,
|
|
|
+ z: position.z,
|
|
|
+ };
|
|
|
+ this.htmlLayer[0].show = true;
|
|
|
+ this.setEveryHTML();
|
|
|
+ },
|
|
|
+ // 点击事件
|
|
|
+ clickEvent: function(event) {
|
|
|
+ event.preventDefault();
|
|
|
+ let vector = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1, 0.5);
|
|
|
+ vector = vector.unproject(camera);
|
|
|
+ let raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
|
|
|
+ let intersects = raycaster.intersectObjects(scene.children, true);
|
|
|
+ console.log(intersects);
|
|
|
+ // 找是否是可点击对象
|
|
|
+ let obj = this.getClickObject(intersects);
|
|
|
+ if (obj) {
|
|
|
+ console.log(obj);
|
|
|
+ this.initCylinderGeometry(obj.position);
|
|
|
+ this.showFanInfo(obj.position);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 初始化
|
|
|
+ initThree: function() {
|
|
|
+ this.initScene();
|
|
|
+ // this.initAxesHelper();
|
|
|
+ this.initCamera();
|
|
|
+ this.initRender();
|
|
|
+ this.initLight();
|
|
|
+ this.initControls();
|
|
|
+ this.initContent();
|
|
|
+ renderer.setAnimationLoop(this.animate);
|
|
|
+ },
|
|
|
+ // 动画
|
|
|
+ animate: function() {
|
|
|
+ var delta = clock.getDelta();
|
|
|
+ for (var i = 0; i < mixers.length; i++) {
|
|
|
+ // 重复播放动画
|
|
|
+ mixers[i].update(delta);
|
|
|
+ }
|
|
|
+ renderer.render(scene, camera);
|
|
|
+ },
|
|
|
},
|
|
|
- // 函数
|
|
|
- methods: {},
|
|
|
// 生命周期钩子
|
|
|
beforeCreate() {
|
|
|
// 创建前
|
|
@@ -224,5 +330,26 @@ export default {
|
|
|
|
|
|
<style lang="less" scoped>
|
|
|
.three-model {
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ .three-html-layer {
|
|
|
+ position: absolute;
|
|
|
+ width: 0;
|
|
|
+ height: 0;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+
|
|
|
+ .three-html-dom {
|
|
|
+ position: absolute;
|
|
|
+ }
|
|
|
+
|
|
|
+ .fan-info {
|
|
|
+ width: 149px;
|
|
|
+ height: 288px;
|
|
|
+ background: #1a1f2fd8;
|
|
|
+ border: 1px solid #05bb4c;
|
|
|
+ box-shadow: 0px 8px 17px 1px #05bb4c66;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|