增加图片支持
This commit is contained in:
parent
3f80854bbc
commit
26eff2fcb3
@ -2,7 +2,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<video
|
<video
|
||||||
id="video"
|
id="video"
|
||||||
src="/static/test.mp4"
|
:src="mediaSource"
|
||||||
style="display: none"
|
style="display: none"
|
||||||
loop
|
loop
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
@ -11,6 +11,18 @@
|
|||||||
<div class="play-button" v-show="!isPlaying" @click="handleClick">
|
<div class="play-button" v-show="!isPlaying" @click="handleClick">
|
||||||
<i class="play-icon"></i>
|
<i class="play-icon"></i>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -19,11 +31,79 @@ import * as THREE from "three";
|
|||||||
import { onMounted, onUnmounted, ref } from "vue";
|
import { onMounted, onUnmounted, ref } from "vue";
|
||||||
|
|
||||||
const isPlaying = ref(false);
|
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 handleClick = () => {
|
||||||
const video = document.getElementById("video") as HTMLVideoElement;
|
if (isVideo.value) {
|
||||||
video.play();
|
const video = document.getElementById("video") as HTMLVideoElement;
|
||||||
isPlaying.value = true;
|
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"),
|
canvas: document.querySelector("#canvas"),
|
||||||
});
|
});
|
||||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
|
||||||
// 获取视频元素
|
// 获取视频元素
|
||||||
const video = document.getElementById("video") as HTMLVideoElement;
|
const video = document.getElementById("video") as HTMLVideoElement;
|
||||||
|
|
||||||
// 创建视频纹理
|
// 创建初始视频纹理
|
||||||
const videoTexture = new THREE.VideoTexture(video);
|
texture.value = new THREE.VideoTexture(video);
|
||||||
videoTexture.minFilter = THREE.LinearFilter;
|
texture.value.minFilter = THREE.LinearFilter;
|
||||||
videoTexture.magFilter = THREE.LinearFilter;
|
texture.value.magFilter = THREE.LinearFilter;
|
||||||
videoTexture.format = THREE.RGBFormat;
|
texture.value.format = THREE.RGBFormat;
|
||||||
|
|
||||||
// 播放视频
|
// 等待视频加载完成后播放
|
||||||
try {
|
video.addEventListener(
|
||||||
await video.play();
|
"loadeddata",
|
||||||
} catch (err) {
|
async () => {
|
||||||
console.error("视频播放失败:", err);
|
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);
|
const geometry = new THREE.PlaneGeometry(10, 10, 200, 200);
|
||||||
@ -161,7 +250,7 @@ onMounted(async () => {
|
|||||||
gl_FragColor = vec4(adjustedColor, texColor.a * alpha * edgeFade);
|
gl_FragColor = vec4(adjustedColor, texColor.a * alpha * edgeFade);
|
||||||
}`,
|
}`,
|
||||||
uniforms: {
|
uniforms: {
|
||||||
mainTexture: { value: videoTexture },
|
mainTexture: { value: texture.value },
|
||||||
mouse: { value: new THREE.Vector2(0, 0) },
|
mouse: { value: new THREE.Vector2(0, 0) },
|
||||||
radius: { value: 0.5 },
|
radius: { value: 0.5 },
|
||||||
time: { value: 0 },
|
time: { value: 0 },
|
||||||
@ -191,8 +280,11 @@ onMounted(async () => {
|
|||||||
function animate() {
|
function animate() {
|
||||||
requestAnimationFrame(animate);
|
requestAnimationFrame(animate);
|
||||||
|
|
||||||
if (video.readyState === video.HAVE_ENOUGH_DATA) {
|
if (texture.value) {
|
||||||
videoTexture.needsUpdate = true;
|
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;
|
material.uniforms.time.value += 0.016;
|
||||||
@ -228,6 +320,17 @@ onUnmounted(() => {
|
|||||||
video.pause();
|
video.pause();
|
||||||
video.src = "";
|
video.src = "";
|
||||||
video.load();
|
video.load();
|
||||||
|
|
||||||
|
// 清理创建的对象URL
|
||||||
|
if (mediaSource.value.startsWith("blob:")) {
|
||||||
|
URL.revokeObjectURL(mediaSource.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理纹理
|
||||||
|
if (texture.value) {
|
||||||
|
texture.value.dispose();
|
||||||
|
texture.value = null;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -267,4 +370,28 @@ canvas {
|
|||||||
border-color: transparent transparent transparent #ffffff;
|
border-color: transparent transparent transparent #ffffff;
|
||||||
margin-left: 8px;
|
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>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user