Appearance
旋转的立方体
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>