Skip to content

Instantly share code, notes, and snippets.

@vaebe
Created May 19, 2025 07:04
Show Gist options
  • Save vaebe/b712c31556002debf4625d2936d7dfac to your computer and use it in GitHub Desktop.
Save vaebe/b712c31556002debf4625d2936d7dfac to your computer and use it in GitHub Desktop.
vue3 组合式函数实现画中画 同时保留原有视频元素,资源消耗过大 待优化,方向 减少每秒渲染帧数、渲染分辨率
// 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