博客地址jsonz1993.github.io/2018/07/vid…html
github地址 欢迎start follow ( *・ω・)✄╰ひ╯git
先看一下效果,原视频是这样的。咱们要实现的效果是这样子的。之因此找这个视频是由于...这个视频和背景的对比度比较高作出来比较有辨识度,没有其余的意思 ( *・ω・)✄╰ひ╯es6
某天一个基友在群里问我,在抖音看到一种视频,问我是否是能实现。我说能够的~ 因而当天晚上花了一个多小时折腾了一个粗糙版本...github
先把视频丢到部门技术群问有没有关键字,给了一个keyword 图片转字符串 因而照着这个思路去gayhub找资源拼乐高!面试
input[type="file"]
获取文件URL.createObjectURL
来获取视频的路径ctx.drawImage
咱们能够把某个 video 当前的图像渲染到 canvas里面ctx.getImageData
能够获取当前canvas 里面图片的色值,利用公式计算出灰度ctx.fillText
重绘进去video.currentTime
来得到视频的某一时刻图像,重复上述重绘过程既然大概的思路已经理清,接下来就是具体的编码,把想法写出来的过程json
首先咱们先肯定下html须要哪些元素canvas
大概是长这样:segmentfault
<input type="file" id="inputFile" accept=".mp4" />
<canvas id="canvasShow"></canvas>
<video id="video"></video>
复制代码
接下来js文件,咱们要先对 input 绑定个监听事件,拿到文件url以后设置给videodom
这里要注意两点,一个是 url
用完不用的话,用 URL.revokeObjectURL
释放资源; 一个是咱们这里用了 await
在domVide.onCanplay以前不作任何操做,防止视频没有加载完就操做,有黑屏风险。async
若是对 es六、es七、es8不熟悉的小伙伴要去补一下了~ 如今基本不会这些基本语法都看不懂demo= = 附上阮一峰老师的ES6教程,又想起面试被问ES7有什么新特性 简直是*了狗
domInput.addEventListener('change', async({target: {files }})=> {
const file = files[0];
const url = URL.createObjectURL(file);
domVideo.src = urlrl;
await new Promise(res=> domVideo.addEventListener('canplay', res));
// next ====> handleVideoInit()
});
复制代码
拿到视频以后,咱们要把当前这一个时刻的图像渲染到canvas里面 先用ctx.drawImage(video, 0, 0, width, height)
把video dom当前屏渲染进canvas
再用ctx.getImageData(0, 0, width, height)
获取图片的色值来作处理
能够经过调整 img2Text
来选择渲染出来的图片是想要怎样的(由哪些字符组成等等)
好比把 textList改成 ['Aa', 'Bv', 'Cc', 'Dd', '#', '&', '@', '$', '*', '?', ';', '^', '·', '·', '·', '·'],辨识度会高一点
/* domVide => video元素 size => 存放video等元素的长宽 canvasVideo => 存放video当前的图像的canvas canvasShow => 存放最后展现效果的canvas */
const size = {w: 0, h: 0};
const canvasVideo = document.createElement('canvas');
function handleVideoInit() {
domVideo.currentTime = 0;
size.w = domVideo.width = canvasVideo.width = canvasShow.width = domVideo.videoWidth * .5;
size.h = domVideo.height = canvasVideo.height = canvasShow.height = domVideo.videoHeight * .5;
video2Img();
}
function video2Img() {
const { w, h } = size;
ctxVideo.drawImage(domVideo, 0, 0, w, h);
const { data } = ctxVideo.getImageData(0, 0, w, h);
ctxShow.clearRect(0, 0, w, h);
for (let _h= 0; _h< h; _h+= 8) {
for (let _w= 0; _w< w; _w+= 8) {
const index = (_w + w * _h) * 4;
const r = data[index + 0];
const g = data[index + 1];
const b = data[index + 2];
const gray = .299 * r + .587 * g + .114 * b;
ctxShow.fillText(img2Text(gray), _w, _h + 8);
}
}
}
function img2Text(g) {
const i = g % 16 === 0 ? parseInt(g / 16) - 1 : parseInt(g/ 16);
return ['#', '&', '@', '%', '$', 'w', '*', '+', 'o', '?', '!', ';', '^', ',', '.', ' '][i];
}
复制代码
到这一步,其实已经实现了把一张图片变为字符填充图了,剩下的工做无非就是把视频变成一张张的图片,而后重复执行这些逻辑
咱们改一下 video2Img 函数,将其实现为能持续调用的形式, 再添加一个函数 clear
用来清理垃圾
这里用到的是 window.requestAnimationFrame 去持续调用
function video2Img({
timePoint= 0,
curT= Date.now(),
prevT= Date.now(),
prevInterval,
}) {
const { w, h } = size;
ctxVideo.drawImage(domVideo, 0, 0, w, h);
drawOnce();
let _interval = Math.max((curT - prevT), 16) / 1000;
if (curT - prevT !== 0) _interval -= prevInterval;
await new Promise(res=> setTimeout(res, _interval*1000));
const nextTimePoint = _interval + timePoint;
if (nextTimePoint > domVideo.duration) return clear();
tId = window.requestAnimationFrame(()=> video2Img({
timePoint: nextTimePoint,
prevT: curT,
curT: Date.now(),
prevInterval: _interval,
}));
}
function drawOnce() {
const { data } = ctxVideo.getImageData(0, 0, w, h);
ctxShow.clearRect(0, 0, w, h);
for (let _h= 0; _h< h; _h+= 8) {
for (let _w= 0; _w< w; _w+= 8) {
const index = (_w + w * _h) * 4;
const r = data[index + 0];
const g = data[index + 1];
const b = data[index + 2];
const gray = .299 * r + .587 * g + .114 * b;
ctxShow.fillText(img2Text(gray), _w, _h + 8);
}
}
}
function cleart() {
const {w, h} = size;
lastUrl && URL.revokeObjectURL(lastUrl);
tId && window.cancelAnimationFrame(tId);
ctxShow.clearRect(0, 0, w, h);
ctxVideo.clearRect(0, 0, w, h);
}
复制代码
至此,功能基本都实现了,下面提供在线的呆毛和github仓库地址~
video转图片忘了是在github看哪一个项目的,ctx.drawImage(video, 0, 0, width, height)
这个是看完才知道的。
图片转字符基本是看这个大哥的github
在找方案的时候看到的一个像素图实现,挺有趣的,之前实现马赛克是拿周围像素值取平均去作,这个哥们是直接放大截图 更简单粗暴传送门