Files
3Dviewer/frontend/src/components/viewer/ViewCube.vue
likegears 7af9c323f6 Initial commit: 3D Viewer application
Features:
- Vue 3 frontend with Three.js/Online3DViewer
- Node.js API with PostgreSQL and Redis
- Python worker for model conversion
- Docker Compose for deployment
- ViewCube navigation with drag rotation and 90° snap
- Cross-section, exploded view, and render settings
- Parts tree with visibility controls

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 14:00:17 +08:00

74 lines
1.8 KiB
Vue

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { useViewerStore } from '@/stores/viewer'
import {
getViewCubeService,
resetViewCubeService,
type ViewDirection,
} from '@/services/viewCubeService'
const viewerStore = useViewerStore()
const containerRef = ref<HTMLElement | null>(null)
let renderLoopId: number | null = null
onMounted(() => {
if (containerRef.value) {
const service = getViewCubeService()
service.initialize(
containerRef.value,
(direction: ViewDirection) => {
viewerStore.animateCameraToView(direction)
},
(deltaX: number, deltaY: number) => {
viewerStore.rotateCamera(deltaX, deltaY)
},
(direction: ViewDirection) => {
// When directly facing a face and clicking it, rotate 90° clockwise
viewerStore.rotateCameraAroundAxis(direction)
}
)
// Start render loop to keep ViewCube in sync with main camera
startRenderLoop()
}
})
function startRenderLoop(): void {
const sync = () => {
const service = getViewCubeService()
if (service.isInitialized() && viewerStore.camera) {
service.syncWithMainCamera(viewerStore.camera)
}
renderLoopId = requestAnimationFrame(sync)
}
renderLoopId = requestAnimationFrame(sync)
}
onUnmounted(() => {
if (renderLoopId !== null) {
cancelAnimationFrame(renderLoopId)
}
resetViewCubeService()
})
</script>
<template>
<div class="viewcube-container" ref="containerRef"></div>
</template>
<style scoped>
.viewcube-container {
position: absolute;
top: 16px;
right: 16px;
width: 100px;
height: 100px;
z-index: 20;
border-radius: 8px;
overflow: hidden;
background: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(4px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
</style>