Skip to content

旋转的立方体

vue
<template>
  <div class="canvas-container">
    <div class="controls">
      <div class="control-item">
        <label>视野角度 (FOV): {{ fov }}</label>
        <input type="range" v-model="fov" min="30" max="120" @input="updateCamera">
      </div>
      <div class="control-item">
        <label>立方体尺寸: {{ boxSize }}</label>
        <input type="range" v-model="boxSize" min="1" max="10" step="0.1" @input="updateCamera">
      </div>
      <div class="control-item">
        <label>形变强度: {{ deformStrength }}</label>
        <input type="range" v-model="deformStrength" min="0" max="1" step="0.01" @input="updateDeformation">
      </div>
    </div>

    <canvas ref="canvasRef"></canvas>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref } from 'vue'
import * as THREE from 'three'

const canvasRef = ref<HTMLCanvasElement | null>(null)
const fov = ref(75)
const near = ref(0.1)
const far = ref(10)
const boxSize = ref(3)
const deformStrength = ref(0)
let camera: THREE.PerspectiveCamera
let cube: THREE.Mesh
let originalVertices: Float32Array | null = null
let geometry: THREE.BoxGeometry

// 更新相机函数
const updateCamera = () => {
  if (!camera) return
  camera.fov = fov.value
  camera.near = near.value
  camera.far = far.value
  camera.updateProjectionMatrix()
  
  // 更新立方体尺寸
  if (cube) {
    cube.scale.set(boxSize.value, boxSize.value, boxSize.value)
  }
}

// 添加更新形变的函数
const updateDeformation = () => {
  if (!geometry || !originalVertices) return
  
  const positions = geometry.attributes.position
  const vertices = positions.array as Float32Array
  
  // 对每个顶点应用随机形变
  for (let i = 0; i < vertices.length; i += 3) {
    vertices[i] = originalVertices[i] + (Math.random() - 0.5) * deformStrength.value
    vertices[i + 1] = originalVertices[i + 1] + (Math.random() - 0.5) * deformStrength.value
    vertices[i + 2] = originalVertices[i + 2] + (Math.random() - 0.5) * deformStrength.value
  }
  
  positions.needsUpdate = true
  geometry.computeVertexNormals() // 重新计算法线
}

onMounted(() => {
  if (!canvasRef.value) return
  
  const canvas = canvasRef.value
  const container = canvas.parentElement!
  
  // 设置 canvas 尺寸为容器大小
  canvas.width = container.clientWidth
  canvas.height = container.clientHeight

  // 创建场景
  const scene = new THREE.Scene()
  
  // 创建相机
  camera = new THREE.PerspectiveCamera(
    fov.value,
    canvas.width / canvas.height,
    near.value,
    far.value
  )
  camera.position.z = 5

  // 创建渲染器
  const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    antialias: true
  })
  renderer.setSize(canvas.width, canvas.height)
  
  // 获取网页背景色并设置为渲染器的清除颜色
  const bgColor = window.getComputedStyle(document.body).backgroundColor
  renderer.setClearColor(bgColor, 1)

  // 创建彩色渐变纹理
  const textureSize = 512
  const canvas2d = document.createElement('canvas')
  canvas2d.width = textureSize
  canvas2d.height = textureSize
  const context = canvas2d.getContext('2d')!
  
  const gradient = context.createLinearGradient(0, 0, textureSize, textureSize)
  gradient.addColorStop(0, '#ff0000')
  gradient.addColorStop(0.5, '#00ff00')
  gradient.addColorStop(1, '#0000ff')
  
  context.fillStyle = gradient
  context.fillRect(0, 0, textureSize, textureSize)
  
  const texture = new THREE.CanvasTexture(canvas2d)

  // 修改立方体创建部分
  geometry = new THREE.BoxGeometry(1, 1, 1, 8, 8, 8) // 增加分段数以获得更多顶点
  const material = new THREE.MeshBasicMaterial({ map: texture })
  cube = new THREE.Mesh(geometry, material)
  
  // 保存原始顶点位置
  originalVertices = new Float32Array(geometry.attributes.position.array)
  
  cube.scale.set(boxSize.value, boxSize.value, boxSize.value)
  scene.add(cube)

  // 动画循环
  function animate() {
    requestAnimationFrame(animate)
    
    // 旋转立方体
    cube.rotation.x += 0.01
    cube.rotation.y += 0.01
    
    renderer.render(scene, camera)
  }
  
  animate()

  // 处理窗口大小变化
  // window.addEventListener('resize', () => {
  //   const newWidth = container.clientWidth
  //   const newHeight = container.clientHeight
    
  //   camera.aspect = newWidth / newHeight
  //   camera.updateProjectionMatrix()
    
  //   renderer.setSize(newWidth, newHeight)
  // })
})
</script>

<style scoped>
.canvas-container {
  width: 100%;
}

canvas {
  width: 100%;
  height: 60vh;
  display: block;
}

.controls {
  display: flex;
  flex-direction: row;
  align-items: center;
  padding: 15px;
  border-radius: 8px;
  gap: 10px;
}

.control-item {
  margin-bottom: 10px;
}

.control-item label {
  display: block;
  margin-bottom: 5px;
}

.control-item input {
  width: 200px;
}
</style>

Last updated: