StlViewer.vue 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. <template>
  2. <div ref="container" class="w-full h-full cursor-move" title="Drag to rotate" />
  3. </template>
  4. <script setup lang="ts">
  5. import { ref, onMounted, onUnmounted } from "vue";
  6. import * as THREE from "three";
  7. import { STLLoader } from "three/addons/loaders/STLLoader.js";
  8. import { OrbitControls } from "three/addons/controls/OrbitControls.js";
  9. interface Props {
  10. file: File;
  11. }
  12. const props = defineProps<Props>();
  13. const container = ref<HTMLElement | null>(null);
  14. let animationId = 0;
  15. onMounted(() => {
  16. if (!container.value) return;
  17. const width = container.value.clientWidth || 80;
  18. const height = container.value.clientHeight || 80;
  19. const scene = new THREE.Scene();
  20. const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 2000);
  21. camera.position.set(0, 0, 150);
  22. const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
  23. renderer.setSize(width, height);
  24. renderer.setClearColor(0x000000, 0); // transparent
  25. container.value.appendChild(renderer.domElement);
  26. const controls = new OrbitControls(camera, renderer.domElement);
  27. controls.enableDamping = true;
  28. controls.autoRotate = true;
  29. controls.autoRotateSpeed = 3.0;
  30. // Add lights
  31. const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 1);
  32. hemiLight.position.set(0, 200, 0);
  33. scene.add(hemiLight);
  34. const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
  35. dirLight.position.set(100, 200, 100);
  36. scene.add(dirLight);
  37. const loader = new STLLoader();
  38. const objectUrl = URL.createObjectURL(props.file);
  39. loader.load(objectUrl, (geometry) => {
  40. geometry.center();
  41. geometry.computeVertexNormals();
  42. const material = new THREE.MeshPhongMaterial({
  43. color: 0x3b82f6,
  44. specular: 0x111111,
  45. shininess: 100
  46. });
  47. const mesh = new THREE.Mesh(geometry, material);
  48. geometry.computeBoundingSphere();
  49. const radius = geometry.boundingSphere?.radius || 50;
  50. const scale = 50 / radius;
  51. mesh.scale.set(scale, scale, scale);
  52. scene.add(mesh);
  53. URL.revokeObjectURL(objectUrl);
  54. });
  55. const animate = () => {
  56. animationId = requestAnimationFrame(animate);
  57. controls.update();
  58. renderer.render(scene, camera);
  59. };
  60. animate();
  61. const handleResize = () => {
  62. if (!container.value) return;
  63. const w = container.value.clientWidth;
  64. const h = container.value.clientHeight;
  65. camera.aspect = w / h;
  66. camera.updateProjectionMatrix();
  67. renderer.setSize(w, h);
  68. };
  69. window.addEventListener("resize", handleResize);
  70. onUnmounted(() => {
  71. cancelAnimationFrame(animationId);
  72. window.removeEventListener("resize", handleResize);
  73. if (renderer.domElement.parentNode) {
  74. renderer.domElement.parentNode.removeChild(renderer.domElement);
  75. }
  76. renderer.dispose();
  77. });
  78. });
  79. </script>