Appearance
vue
<template>
<!-- 摄像头拍摄到的视频 -->
<video autoplay ref="cameraRef" hidden></video>
<!-- 替换摄像头背景后的视频 -->
<video autoplay ref="cameraChromaRef"></video>
<!-- 渲染摄像头拍摄到的视频 -->
<canvas ref="canvasRef" hidden></canvas>
<!-- 渲染替换摄像头背景后的视频 -->
<canvas ref="canvas2Ref" hidden></canvas>
<!-- 背景图片转为 imageData 的容器 -->
<canvas ref="canvas3Ref" hidden></canvas>
</template>
<script lang="ts" setup>
import { useImageToImageData } from '@/hooks'
import { onMounted, ref } from 'vue'
const cameraRef = ref(null)
const cameraChromaRef = ref(null)
const canvasRef = ref(null)
const canvas2Ref = ref(null)
const canvas3Ref = ref(null)
const init = () => {
const camera = cameraRef.value
const cameraChroma = cameraChromaRef.value
const canvas = canvasRef.value
const canvas2 = canvas2Ref.value
const context = canvas.getContext('2d')
const context2 = canvas2.getContext('2d')
try {
const stream = navigator.mediaDevices.getUserMedia({
video: true,
audio: false
})
camera.srcObject = stream
} catch (error) {
console.error(error)
return
}
// 需要给 video 元素设置 autoplay 属性或手动 camera.play() 才会触发回调,
// 现代浏览器通常禁止自动播放无静音的视频,需要给 video 元素设置 muted 属性
// 或 getUserMedia不采集音频
camera.addEventListener('play', async () => {
const { videoWidth, videoHeight } = camera
canvas.width = canvas2.width = videoWidth
canvas.height = canvas2.height = videoHeight
const stream = canvas2.captureStream()
cameraChroma.srcObject = stream
const imageData = await getImageData()
const timer = setInterval(() => {
const { paused, ended } = camera
if (paused || ended) {
clearInterval(timer)
return
}
updateBg(
context,
context2,
camera,
videoWidth,
videoHeight,
imageData.data
)
}, 50)
})
}
const getImageData = async () => {
const img = await new Promise<HTMLImageElement>(resolve => {
const image = new Image()
image.src = '/images/beach.jpg'
image.onload = () => resolve(image)
})
return useImageToImageData(canvas3Ref.value, img)
}
const updateBg = (
context,
context2,
camera,
videoWidth,
videoHeight,
bgData
) => {
context.drawImage(camera, 0, 0, videoWidth, videoHeight)
const imageData = context.getImageData(0, 0, videoWidth, videoHeight)
const { data } = imageData
for (let i = 0, l = data.length; i < l; i += 4) {
const r = data[i]
const g = data[i + 1]
const b = data[i + 2]
// 偏白色的部分用背景替换
if (r > 100 && g > 100 && b > 100) {
data[i] = bgData[i]
data[i + 1] = bgData[i + 1]
data[i + 2] = bgData[i + 2]
}
}
context2.putImageData(imageData, 0, 0)
}
onMounted(() => {
// init()
})
</script>