Created
May 19, 2025 07:04
-
-
Save vaebe/b712c31556002debf4625d2936d7dfac to your computer and use it in GitHub Desktop.
vue3 组合式函数实现画中画 同时保留原有视频元素,资源消耗过大 待优化,方向 减少每秒渲染帧数、渲染分辨率
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// todo 同时只能有一个元素进入画中画 | |
export function useFloatingDiv() { | |
const video = shallowRef<HTMLVideoElement | null>(null) | |
let animationFrameId: number | null = null | |
function cleanUp() { | |
// 取消动画循环 | |
if (animationFrameId !== null) { | |
cancelAnimationFrame(animationFrameId) | |
animationFrameId = null | |
} | |
// 释放视频资源 | |
if (video.value) { | |
const stream = video.value.srcObject as MediaStream | |
if (stream) { | |
stream.getTracks().forEach(track => track.stop()) | |
} | |
video.value.srcObject = null | |
} | |
} | |
async function open(target: HTMLVideoElement) { | |
// 关闭可能存在的之前实例 | |
await close() | |
// 创建canvas并设置尺寸 | |
const canvas = document.createElement('canvas') | |
canvas.width = target.clientWidth | |
canvas.height = target.clientHeight | |
const ctx = canvas.getContext('2d') | |
if (!ctx) { | |
ElMessage.error('Floating Div 无法获取 Canvas 上下文') | |
return | |
} | |
// 绘制函数 | |
const draw = () => { | |
ctx.clearRect(0, 0, canvas.width, canvas.height) | |
ctx.drawImage(target, 0, 0, canvas.width, canvas.height) | |
animationFrameId = requestAnimationFrame(draw) | |
} | |
// 开始绘制 | |
draw() | |
// 创建并配置视频元素 | |
video.value = document.createElement('video') | |
video.value.srcObject = canvas.captureStream() | |
video.value.muted = true | |
video.value.autoplay = true | |
try { | |
// 等待视频元数据加载 | |
await new Promise<void>((resolve, reject) => { | |
if (!video.value) { | |
return reject(new Error('视频元素不存在')) | |
} | |
video.value.onloadedmetadata = () => { | |
video.value?.play().then(resolve).catch(reject) | |
} | |
// 添加错误处理 | |
video.value.onerror = () => reject(new Error('视频加载失败')) | |
}) | |
// 进入画中画模式 | |
await video.value.requestPictureInPicture() | |
} | |
catch (error) { | |
console.error('启动画中画模式失败:', error) | |
ElMessage.error('启动画中画模式失败') | |
cleanUp() | |
} | |
} | |
async function close() { | |
if (document.pictureInPictureElement) { | |
try { | |
await document.exitPictureInPicture() | |
} | |
catch (error) { | |
console.error('关闭画中画模式失败:', error) | |
} | |
} | |
cleanUp() | |
} | |
async function toggle(target: HTMLVideoElement) { | |
if (document.pictureInPictureElement) { | |
await close() | |
} | |
else { | |
await open(target) | |
} | |
} | |
// 组件卸载时关闭画中画 | |
onUnmounted(() => { | |
close() | |
}) | |
return { open, close, toggle } | |
} | |
// 使用 | |
const { toggle: toggleFloatingDiv } = useFloatingDiv() | |
function togglePictureInPicture() { | |
const videoDom = document.getElementById(`${loginResData.value.id}`) as HTMLVideoElement | |
if (videoDom) { | |
toggleFloatingDiv(videoDom) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment