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);
+}