增加图片支持
This commit is contained in:
parent
3f80854bbc
commit
26eff2fcb3
@ -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 = () => {
|
||||
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<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;
|
||||
|
||||
// 播放视频
|
||||
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;
|
||||
}
|
||||
});
|
||||
</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>
|
||||
|
Loading…
Reference in New Issue
Block a user