diff --git a/src/pages/pointShader/pointShader.vue b/src/pages/pointShader/pointShader.vue index 3de668b..7d79d26 100644 --- a/src/pages/pointShader/pointShader.vue +++ b/src/pages/pointShader/pointShader.vue @@ -2,7 +2,7 @@
+
+ + +
@@ -19,11 +31,79 @@ import * as THREE from "three"; import { onMounted, onUnmounted, ref } from "vue"; const isPlaying = ref(false); +const fileInput = ref(null); +const mediaSource = ref("/static/test.mp4"); +const isVideo = ref(true); +const texture = ref(null); + +const triggerFileUpload = () => { + fileInput.value?.click(); +}; const handleClick = () => { - const video = document.getElementById("video") as HTMLVideoElement; - video.play(); - isPlaying.value = true; + if (isVideo.value) { + const video = document.getElementById("video") as HTMLVideoElement; + video.play(); + isPlaying.value = true; + } +}; + +const createImageTexture = (url: string): Promise => { + return new Promise((resolve) => { + const loader = new THREE.TextureLoader(); + loader.load(url, (texture) => { + texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; + resolve(texture); + }); + }); +}; + +const handleFileUpload = async (event: Event) => { + const input = event.target as HTMLInputElement; + const file = input.files?.[0]; + + if (file) { + const url = URL.createObjectURL(file); + const video = document.getElementById("video") as HTMLVideoElement; + + // 先暂停当前播放 + video.pause(); + isPlaying.value = false; + + if (file.type.startsWith("video/")) { + isVideo.value = true; + // 更新视频源 + mediaSource.value = url; + + // 如果是视频,等待加载后再播放 + video.addEventListener( + "loadeddata", + async () => { + try { + texture.value = new THREE.VideoTexture(video); + texture.value.minFilter = THREE.LinearFilter; + texture.value.magFilter = THREE.LinearFilter; + texture.value.format = THREE.RGBFormat; + await video.play(); + isPlaying.value = true; + } catch (err) { + console.error("视频播放失败:", err); + isPlaying.value = false; + } + }, + { once: true } + ); + } else if (file.type.startsWith("image/")) { + isVideo.value = false; + mediaSource.value = ""; + // 创建图片纹理 + texture.value = await createImageTexture(url); + } + + // 重置文件输入,允许重复上传相同文件 + input.value = ""; + } }; // 创建场景和相机 @@ -41,21 +121,30 @@ onMounted(async () => { canvas: document.querySelector("#canvas"), }); renderer.setSize(window.innerWidth, window.innerHeight); + // 获取视频元素 const video = document.getElementById("video") as HTMLVideoElement; - // 创建视频纹理 - const videoTexture = new THREE.VideoTexture(video); - videoTexture.minFilter = THREE.LinearFilter; - videoTexture.magFilter = THREE.LinearFilter; - videoTexture.format = THREE.RGBFormat; + // 创建初始视频纹理 + texture.value = new THREE.VideoTexture(video); + texture.value.minFilter = THREE.LinearFilter; + texture.value.magFilter = THREE.LinearFilter; + texture.value.format = THREE.RGBFormat; - // 播放视频 - try { - await video.play(); - } catch (err) { - console.error("视频播放失败:", err); - } + // 等待视频加载完成后播放 + video.addEventListener( + "loadeddata", + async () => { + try { + await video.play(); + isPlaying.value = true; + } catch (err) { + console.error("视频播放失败:", err); + isPlaying.value = false; + } + }, + { once: true } + ); // 创建平面几何体 const geometry = new THREE.PlaneGeometry(10, 10, 200, 200); @@ -161,7 +250,7 @@ onMounted(async () => { gl_FragColor = vec4(adjustedColor, texColor.a * alpha * edgeFade); }`, uniforms: { - mainTexture: { value: videoTexture }, + mainTexture: { value: texture.value }, mouse: { value: new THREE.Vector2(0, 0) }, radius: { value: 0.5 }, time: { value: 0 }, @@ -191,8 +280,11 @@ onMounted(async () => { function animate() { requestAnimationFrame(animate); - if (video.readyState === video.HAVE_ENOUGH_DATA) { - videoTexture.needsUpdate = true; + if (texture.value) { + if (isVideo.value && video.readyState === video.HAVE_ENOUGH_DATA) { + (texture.value as THREE.VideoTexture).needsUpdate = true; + } + material.uniforms.mainTexture.value = texture.value; } material.uniforms.time.value += 0.016; @@ -228,6 +320,17 @@ onUnmounted(() => { video.pause(); video.src = ""; video.load(); + + // 清理创建的对象URL + if (mediaSource.value.startsWith("blob:")) { + URL.revokeObjectURL(mediaSource.value); + } + + // 清理纹理 + if (texture.value) { + texture.value.dispose(); + texture.value = null; + } }); @@ -267,4 +370,28 @@ canvas { border-color: transparent transparent transparent #ffffff; margin-left: 8px; } + +.upload-container { + position: fixed; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + z-index: 1000; +} + +.upload-btn { + background: rgba(0, 0, 0, 0.6); + color: white; + border: none; + padding: 10px 20px; + border-radius: 20px; + cursor: pointer; + font-size: 14px; + transition: all 0.3s; +} + +.upload-btn:hover { + background: rgba(0, 0, 0, 0.8); + transform: scale(1.05); +}