Files
portfolio/app/components/ShrimpRender.vue
2026-01-29 21:23:52 -05:00

117 lines
3.0 KiB
Vue

<template>
<div ref="containerRef" class="shrimp w-full h-full cursor-pointer" @click="toggleRotation"></div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { AsciiEffect } from 'three/examples/jsm/effects/AsciiEffect.js'
const containerRef = ref<HTMLDivElement | null>(null)
const isRotating = ref(false)
let scene: THREE.Scene
let camera: THREE.PerspectiveCamera
let renderer: THREE.WebGLRenderer
let effect: AsciiEffect
let model: THREE.Group
let animationId: number
const toggleRotation = () => {
isRotating.value = !isRotating.value
}
onMounted(() => {
if (!containerRef.value) return
// Scene setup
scene = new THREE.Scene()
scene.background = null
// Camera
camera = new THREE.PerspectiveCamera(
60,
containerRef.value.clientWidth / containerRef.value.clientHeight,
1,
1000
)
camera.position.set(0, 50, 15)
camera.lookAt(0, 0, 0)
// Renderer
renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(containerRef.value.clientWidth, containerRef.value.clientHeight)
renderer.setPixelRatio(window.devicePixelRatio)
// ASCII Effect
effect = new AsciiEffect(renderer, ' .:-=+*#%@', { invert: false })
effect.setSize(containerRef.value.clientWidth, containerRef.value.clientHeight)
effect.domElement.style.color = 'rgba(0, 255, 65, 0.7)'
effect.domElement.style.backgroundColor = 'transparent'
containerRef.value.appendChild(effect.domElement)
// Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, Math.PI / 2)
scene.add(ambientLight)
// Load GLTF model
const loader = new GLTFLoader()
loader.load('/shrimp_smol.glb', (gltf) => {
model = new THREE.Group()
model.position.set(0, -1, 0)
// Find and add the mesh
const mesh = gltf.scene.getObjectByName('Mesh_Mesh_head_geo001_lambert2SG001')
if (mesh) {
mesh.rotation.x = -Math.PI / 2
model.add(mesh)
}
scene.add(model)
})
// Animation loop
const animate = () => {
animationId = requestAnimationFrame(animate)
if (model) {
model.rotation.z += 0.01
}
effect.render(scene, camera)
}
animate()
// Handle window resize
const handleResize = () => {
if (!containerRef.value) return
camera.aspect = containerRef.value.clientWidth / containerRef.value.clientHeight
camera.updateProjectionMatrix()
renderer.setSize(containerRef.value.clientWidth, containerRef.value.clientHeight)
effect.setSize(containerRef.value.clientWidth, containerRef.value.clientHeight)
}
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
if (animationId) {
cancelAnimationFrame(animationId)
}
if (effect && effect.domElement && containerRef.value) {
containerRef.value.removeChild(effect.domElement)
}
window.removeEventListener('resize', () => {})
})
</script>
<style scoped>
.shrimp {
min-height: 400px;
}
</style>