博客地址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
在找方案的時候看到的一個像素圖實現,挺有趣的,之前實現馬賽克是拿周圍像素值取平均去作,這個哥們是直接放大截圖 更簡單粗暴傳送門