增加图片支持

This commit is contained in:
kura 2025-01-15 15:39:55 +08:00
parent 3f80854bbc
commit 26eff2fcb3

View File

@ -2,7 +2,7 @@
<div>
<video
id="video"
src="/static/test.mp4"
:src="mediaSource"
style="display: none"
loop
crossorigin="anonymous"
@ -11,6 +11,18 @@
<div class="play-button" v-show="!isPlaying" @click="handleClick">
<i class="play-icon"></i>
</div>
<div class="upload-container">
<input
type="file"
ref="fileInput"
@change="handleFileUpload"
accept="image/*,video/*"
style="display: none"
/>
<button class="upload-btn" @click="triggerFileUpload">
上传图片/视频
</button>
</div>
</div>
</template>
@ -19,11 +31,79 @@ import * as THREE from "three";
import { onMounted, onUnmounted, ref } from "vue";
const isPlaying = ref(false);
const fileInput = ref<HTMLInputElement | null>(null);
const mediaSource = ref("/static/test.mp4");
const isVideo = ref(true);
const texture = ref<THREE.VideoTexture | THREE.Texture | null>(null);
const triggerFileUpload = () => {
fileInput.value?.click();
};
const handleClick = () => {
if (isVideo.value) {
const video = document.getElementById("video") as HTMLVideoElement;
video.play();
isPlaying.value = true;
}
};
const createImageTexture = (url: string): Promise<THREE.Texture> => {
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;
//
//
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;
}
});
</script>
@ -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);
}
</style>