canvas 实现视频帧截取为封面图与证件照效果

77 阅读1分钟

知识点

  • canvas
    1. drawImage

    2. getImageData

    3. putImageData

    我们可以通过getImageData方法来取得所有的像素点的rgba数据。 使用ctx.getImageData()获取所有的数据,打印出来是一个数组:

image

就像这样,像素点的数量是上述数组的长度 / 4。为什么是除以4,是因为像素点的rgba是4个值,这个数组从0开始每隔4个是一个像素点的r/g/b/a值。所以数组的第0,1,2,3个值分别代表第一个像素点的r,g,b,a的值。

图像的像素的呈现是通过rgb三原色+ alpha透明度实现的,所以frameData数组中的数值都是不超过255的数值。 alpha透明度 也对应用了0-255去表示(对应常规理解的 0-1)。

  • 使用[requestAnimationFrame](url)代替setTimeout实现连续播放效果

这就能找喜欢的视频帧frame通过点击canvas元素右键存储图(png格式)

  • 整体效果图代码如下:
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <style>
            body {
                background: black;
                color: #cccccc;
            }
            #c2 {
                background-image: url(zzz.png);
                background-repeat: no-repeat;
            }
            div {
                float: left;
                border: 1px solid #666;
                padding: 10px;
                margin: 10px;
                background: #3951d6;
            }
        </style>
        <script type="text/javascript" src="main.js"></script>
    </head>

    <body onload="processor.doLoad()">
        <div>
            <video id="video" src="video.ogv" controls="true"></video>
        </div>
        <div>
            <canvas id="c1" width="160" height="96"></canvas>
            <canvas id="c2" width="160" height="96"></canvas>
            <img id="img" />
        </div>
    </body>
</html>
const processor = {
	animationFrame() {
            this.createFrame();
            requestAnimationFrame(() => this.animationFrame());
	},
	doLoad() {
            this.c1 = document.querySelector("#c1");
            this.c2 = document.querySelector("#c2");
            this.ctx1 = this.c1.getContext("2d");
            this.ctx2 = this.c2.getContext("2d");
            this.video = document.querySelector("#video");
            this.videoWidth = this.video.videoWidth;
            this.videoHeight = this.video.videoHeight;
            this.c1w = this.videoWidth * 0.5;
            this.c1h = this.videoHeight * 0.5;

            const self = this;
            this.video.addEventListener(
                "play",
                () => {
                    self.makePicture();
                    self.animationFrame();
                },
                false
            );
	},
	createFrame() {
            this.ctx1.drawImage(this.video, 0, 0, this.c1w, this.c1h);
            const frame = this.ctx1.getImageData(0, 0, this.c1w, this.c1h);
            const frameData = frame.data;
            const len = frameData.length / 4;

            for (let i = 0; i < len; i++) {
                const r = frameData[i * 4 + 0];
                const g = frameData[i * 4 + 1];
                const b = frameData[i * 4 + 2];
                if (r > 100 && g > 100 && b < 43) frame.data[i * 4 + 3] = 0;
            }

            this.ctx2.putImageData(frame, 0, 0);
	},
        // 封面图
        makePicture() {
            this.ctx1.drawImage(this.video, 0, 0, this.c1w, this.c1h);
            const imgUrl = this.c1.toDataURL("image/png");
            document.querySelector("#img").setAttribute("src", imgUrl);
	},
};

该项目需跑到服务下,通过vscode的插件live server 跑下服务就行