前面已经发过一篇,抖音「延迟像素格」的实现思路
今天又来蹭热度了,最近的抖音「蓝线挑战」的道具使用人数多起来, 什么发际线了,光头效果了,曲线眉毛了等等等等,有点意思,想了想前端应该也可以实现,今天这篇文章就整理下。
方案构思
- 采集视频
- 根据视频分辨率,设置绘制区域,保持统一
- 随着时间推移绘制视频指定区域
- 一部分绘制图片,一部分绘制视频
- 添加辅助线
- 整体绘制结束
这里要注意的是绘制成图片的位置,第二次就不再绘制该坐标区域
采集区域和绘制区域保持一致,视频尺寸和画布尺寸保持一致。
第一次:采集视频位置 (0,0,w=videowidth,h=1) 绘制成图片的位置(0,0, w=videowidth,h=1),
第二次:采集视频位置 (0,1,w=videowidth,h=1) 绘制成图片的位置(0,1, w=videowidth,h=1),
第二次:采集视频位置 (0,2,w=videowidth,h=1) 绘制成图片的位置(0,2, w=videowidth,h=1),
…
每次绘制之后在下面一行的位置绘制上辅助线,用于告诉用户绘制到的位置
最后贴上体验地址和全部代码
https://ibeeger.com/record/canvas.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>抖音蓝线挑战</title>
<style>
*{margin: 0; padding: 0;}
body,html{overflow: hidden}
section{
display: flex;
width: 100%;
height: 100%;
flex-direction: row;
}
video{width:50%; background-color: #eee}
canvas{
width: 50%;
}
</style>
</head>
<body>
<section>
<canvas></canvas>
<video id='vd'></video>
</section>
<script>
(function(){
var cs = document.querySelector('canvas')
var ctx = cs.getContext('2d')
var vd = document.getElementById('vd')
var req;
var heightstep = 0;
cs.width = window.innerWidth/2
cs.height = window.innerHeight
async function start(){
let constraints = {video:true}
let stream = await navigator.mediaDevices.getUserMedia(constraints)
vd.srcObject = stream
vd.addEventListener('loadeddata', function(){
vd.play();
cs.width = vd.videoWidth;
cs.height = vd.videoHeight;
draw()
})
}
function draw(){
if(heightstep >= cs.height-1){
cancelAnimationFrame(req);
return;
}
heightstep++;
let _h = heightstep % vd.videoHeight;
ctx.drawImage(vd,0,_h,vd.videoWidth,_h+1, 0, _h, cs.width, _h+1);
ctx.drawImage(vd,0,_h+1, vd.videoWidth, vd.videoHeight, 0, _h+1, cs.width, cs.height);
ctx.strokeStyle ="#0c0"
ctx.beginPath()
ctx.moveTo(0,_h+2)
ctx.lineTo(cs.width, _h+2);
ctx.closePath();
ctx.stroke();
req = requestAnimationFrame(draw)
}
start();
})()
</script>
</body>
</html>