middle3D.vue 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. <template>
  2. <div style="position: relative">
  3. <div ref="containerRef" id="middle3D"></div>
  4. </div>
  5. </template>
  6. <script setup>
  7. import { getOnlineInfo } from "@/api/bigScreen";
  8. import * as THREE from "three";
  9. import * as TWEEN from "three/examples/jsm/libs/tween.module.js";
  10. import gsap from "gsap";
  11. //导入轨道控制器
  12. import { OrbitControls } from "three/addons/controls/OrbitControls.js";
  13. import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
  14. import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
  15. const containerRef = ref(null);
  16. //相机y轴高度
  17. const cameraYVal = ref(8);
  18. //相机x轴半宽度
  19. const cameraXVal = ref(20);
  20. //相机z轴高度
  21. const cameraZVal = ref(6);
  22. const modleColor = ref({
  23. online: "0x06ffa5",
  24. outline: "0xdb4848",
  25. });
  26. const infoArray = ref([]);
  27. const getInfoArray = async () => {
  28. const { data } = await getOnlineInfo();
  29. infoArray.value = data;
  30. };
  31. const init3D = () => {
  32. //创建场景
  33. const scene = new THREE.Scene();
  34. //渲染函数
  35. const render = () => {
  36. renderer.render(scene, camera);
  37. requestAnimationFrame(render);
  38. TWEEN.update();
  39. };
  40. //灯光
  41. const lights = () => {
  42. //给场景增加环境光
  43. let Light1 = new THREE.DirectionalLight(0xffffff, 1);
  44. Light1.position.set(0, 0, 10);
  45. scene.add(Light1);
  46. let Light2 = new THREE.DirectionalLight(0xffffff, 1);
  47. Light2.position.set(0, 0, -10);
  48. scene.add(Light2);
  49. let Light3 = new THREE.DirectionalLight(0xffffff, 1);
  50. Light3.position.set(10, 0, 0);
  51. scene.add(Light3);
  52. let Light4 = new THREE.DirectionalLight(0xffffff, 1);
  53. Light4.position.set(-10, 0, 10);
  54. scene.add(Light4);
  55. let Light5 = new THREE.DirectionalLight(0xffffff, 1);
  56. Light5.position.set(0, 10, 0);
  57. scene.add(Light5);
  58. let Light6 = new THREE.DirectionalLight(0xffffff, 1);
  59. Light6.position.set(0, -10, 0);
  60. scene.add(Light6);
  61. let Light7 = new THREE.DirectionalLight(0xffffff, 1);
  62. Light7.position.set(5, 10, 0);
  63. scene.add(Light7);
  64. let Light8 = new THREE.DirectionalLight(0xffffff, 1);
  65. Light8.position.set(0, 10, 5);
  66. scene.add(Light8);
  67. let Light9 = new THREE.DirectionalLight(0xffffff, 1);
  68. Light9.position.set(-5, 10, 0);
  69. scene.add(Light9);
  70. };
  71. lights();
  72. //创建一个相机
  73. const camera = new THREE.PerspectiveCamera(
  74. 45, //视角
  75. document.getElementById("middle3D").clientWidth /
  76. document.getElementById("middle3D").clientHeight,
  77. 0.1, //近平面
  78. 1000 //远平面
  79. );
  80. camera.position.set(0, 15, 8);
  81. const renderer = new THREE.WebGLRenderer({
  82. //抗锯齿
  83. antialias: true,
  84. });
  85. renderer.setSize(
  86. document.getElementById("middle3D").clientWidth,
  87. document.getElementById("middle3D").clientHeight
  88. );
  89. containerRef.value.appendChild(renderer.domElement);
  90. scene.fog = new THREE.Fog(0x000000, 0.1, 50);
  91. scene.background = new THREE.Color(0x000000);
  92. //
  93. const controls = new OrbitControls(camera, renderer.domElement);
  94. controls.update();
  95. const loader = new GLTFLoader();
  96. render();
  97. //圆锥体动画
  98. function animateModel(model) {
  99. // 创建一个时间轴
  100. const tl = gsap.timeline({
  101. repeat: -1, // 无限重复
  102. yoyo: true, // 往复运动
  103. ease: "power1.inOut", // 缓动函数
  104. });
  105. // 添加动画到时间轴
  106. tl.to(model.position, {
  107. duration: 1, // 动画持续时间
  108. y: 4, // 上升到y=3.5的位置
  109. ease: "power1.inOut",
  110. });
  111. }
  112. //单个巡航实现函数
  113. function timeout(time, option, i) {
  114. setTimeout(() => {
  115. camera.position.set(
  116. cameraPositions[i].reset.x,
  117. cameraYVal.value,
  118. cameraPositions[i].reset.z
  119. );
  120. camera.lookAt(0, 0, 0);
  121. camera.position.set(
  122. cameraPositions[i].start.x,
  123. cameraYVal.value,
  124. cameraPositions[i].start.z
  125. );
  126. const tl = gsap.timeline({
  127. defaults: { ease: "linear" },
  128. });
  129. tl.to(camera.position, option);
  130. }, time);
  131. }
  132. //车间巡航动画
  133. function animateCamera() {
  134. let timer = 0;
  135. for (let i = 0; i < cameraPositions.length; i++) {
  136. // 创建一个时间轴
  137. if (i !== 0) {
  138. timer = timer + cameraPositions[i - 1].time * 1000;
  139. }
  140. const object = {
  141. duration: cameraPositions[i].time,
  142. [cameraPositions[i].change.lable]: cameraPositions[i].change.value,
  143. };
  144. timeout(timer, object, i);
  145. }
  146. // 添加动画到时间轴
  147. }
  148. // const gui = new GUI();
  149. // gui.add(camera.position, "x").min(-50).max(50).step(0.1).name("相机x");
  150. // gui.add(camera.position, "y").min(-50).max(50).step(0.1).name("相机y");
  151. // gui.add(camera.position, "z").min(-50).max(50).step(0.1).name("相机z");
  152. // gui.add(camera, "fov").min(0).max(50).step(0.1).name("相机视角");
  153. //加载模型
  154. loader.load("/3dmodel/XF.glb", (gltf) => {
  155. console.log(gltf);
  156. scene.add(gltf.scene);
  157. gltf.scene.position.set(2.8, 0, 0);
  158. const motorModel = gltf.scene;
  159. motorModel.traverse((obj) => {
  160. if (/^锥体-/.test(obj.name)) {
  161. obj.material.color.set(0x06ffa5);
  162. animateModel(obj);
  163. }
  164. });
  165. animateCamera();
  166. setInterval(() => {
  167. animateCamera();
  168. }, 30000);
  169. });
  170. //巡航路线 ->reset 为调整相机lookat向量点 start 为调整后相机位开始位置 change 为本次动画调整的坐标和距离
  171. const cameraPositions = [
  172. {
  173. reset: {
  174. x: 0,
  175. z: cameraZVal.value,
  176. },
  177. start: {
  178. x: cameraXVal.value,
  179. z: cameraZVal.value,
  180. },
  181. change: {
  182. lable: "x",
  183. value: -cameraXVal.value,
  184. },
  185. time: 10,
  186. },
  187. {
  188. reset: {
  189. x: -cameraXVal.value,
  190. z: 0,
  191. },
  192. start: {
  193. x: -cameraXVal.value,
  194. z: cameraZVal.value,
  195. },
  196. change: {
  197. lable: "z",
  198. value: -cameraZVal.value,
  199. },
  200. time: 5,
  201. },
  202. {
  203. reset: {
  204. x: 0,
  205. z: -cameraZVal.value,
  206. },
  207. start: {
  208. x: -cameraXVal.value,
  209. z: -cameraZVal.value,
  210. },
  211. change: {
  212. lable: "x",
  213. value: cameraXVal.value,
  214. },
  215. time: 10,
  216. },
  217. {
  218. reset: {
  219. x: cameraXVal.value,
  220. z: 0,
  221. },
  222. start: {
  223. x: cameraXVal.value,
  224. z: -cameraZVal.value,
  225. },
  226. change: {
  227. lable: "z",
  228. value: cameraZVal.value,
  229. },
  230. time: 5,
  231. },
  232. ];
  233. //监听窗口变化
  234. window.addEventListener("resize", () => {
  235. renderer.setSize(
  236. document.getElementById("middle3D").clientWidth,
  237. document.getElementById("middle3D").clientHeight
  238. );
  239. camera.aspect =
  240. document.getElementById("middle3D").clientWidth /
  241. document.getElementById("middle3D").clientHeight;
  242. camera.updateProjectionMatrix();
  243. });
  244. };
  245. onMounted(() => {
  246. getInfoArray();
  247. init3D();
  248. });
  249. </script>
  250. <style lang="scss" scoped>
  251. #middle3D {
  252. height: 29vh;
  253. width: 100%;
  254. position: absolute;
  255. z-index: 5;
  256. }
  257. </style>