html:
<body onload="processor.doLoad()">
<div>
<video id="video" src="./video/video.ogv" controls="true" />
</div>
<div>
<!-- 显示原视频的当前帧 -->
<canvas id="c1" width="160" height="96"></canvas>
<!-- 显示绿幕替换后的图像 -->
<canvas id="c2" width="160" height="96"></canvas>
</div>
</body>
css:
body {
background: black;
color: #cccccc;
}
#c2 {
background-image: url('./video/bg1.jpg');
background-repeat: no-repeat;
}
div {
float: left;
border: 1px solid #444444;
padding: 10px;
margin: 10px;
background: #3b3b3b;
}
js:
let processor = {
timerCallback: function () {
if (this.video.paused || this.video.ended) {
/* 判断视频是否处于正常播放状态 */
return;
}
this.computeFrame();
let self = this;
setTimeout(function () {
/* 递归调用,视频播放时循环执行 */
self.timerCallback();
}, 0);
},
doLoad: function () {
/* 找到对应元素 */
this.video = document.getElementById('video');
this.c1 = document.getElementById('c1');
this.ctx1 = this.c1.getContext('2d');
this.c2 = document.getElementById('c2');
this.ctx2 = this.c2.getContext('2d');
let self = this;
/* 点击播放时的回调(只在点击播放时调用一次) */
this.video.addEventListener(
'play',
function () {
console.log('play');
self.width = self.video.videoWidth / 2;
self.height = self.video.videoHeight / 2;
self.timerCallback();
},
false
);
},
computeFrame: function () {
/* 将当前video播放的帧绘制到第一个canvas上 */
this.ctx1.drawImage(this.video, 0, 0, this.width, this.height);
/* 取得第一个canvas的当前图像 */
let frame = this.ctx1.getImageData(0, 0, this.width, this.height);
/**
* imageData.data.length用来读取像素数组的大小
* 这里为什么要除以4:
* 因为 frame.data 是一个包含了当前canvas的所有像素点颜色信息的一维数组。
* 比如这里的canvas只有4个像素点,那么frame.data就会是这样的:
* [
* 212,
* 213,
* 24,
* 0,
* 212,
* 213,
* 24,
* 0,
* 212,
* 213,
* 24,
* 0,
* 213,
* 214,
* 25,
* 0
* ]
* 数组的长度是4 * 4 ,每四个元素就是一个像素点的r,g,b,a值
* 所以在这里除以4,得到的就是像素点的数量。在下面乘以4,得到的就是每个像素点的rgba的r的值。
*/
let l = frame.data.length / 4;
console.log('f----', frame.data);
for (let i = 0; i < l; i++) {
let r = frame.data[i * 4 + 0];
let g = frame.data[i * 4 + 1];
let b = frame.data[i * 4 + 2];
/* 这里当当前像素点是黄色时,将这个点的透明度设置为0。
* 需要注意的是:这里的alpha不是0-1,而是0-255,所以如果需要设置透明度为0.5,就不能写0.5,而是255 * 0.5
*/
if (g > 100 && r > 100 && b < 43) frame.data[i * 4 + 3] = 0;
}
/* 除了人头像部分,video其他部分的透明度是0,因此人头像显示在背景图片上方 */
/* 在场景内左上角绘制myImageData代表的图片 */
this.ctx2.putImageData(frame, 0, 0);
return;
},
};
最终效果: