感謝各位在前端一直努力奮鬥的程序猿們,但願之後的HelloWorld會更美好。css
感謝來看看機智的前端童鞋怎麼防盜做者的分享html
感謝停了一天的網終於恢復前端
先來分享2張圖
1)王的憤怒
2) 程序猿的憤怒git
經過啓用瀏覽器攝像的方式,把每一幀的圖映射到canvas上,經過比較上一幀與當前幀的差別,算出來的差別佔的百分比,超過某個百分比就觸發函數。github
栗子:我搖動個人雙手,兩個幀的手的位置不同,從而差別佔的比例就挺大的,就觸發了寫好的回調函數{web
給水杯添加一個動畫 給body更換一個背景顏色
}
程序猿的憤怒是經過在來看看機智的前端童鞋怎麼防盜借鑑的代碼實現的,
原做者項目GitHub地址
同時,在不破壞原做者源碼的基礎下,憤怒的程序猿代碼以下:chrome
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>憤怒的程序猿</title> <style type="text/css"> .room{ text-align: center; margin: 200px 10px; position: relative; height: 240px; width: 320px; display: inline-block; } .room>*{ float: left; position: absolute; bottom: 0; left: 100px; } .room .beizi{ width: 50px; height: 100px; border-radius: 10px; background-color: darkolivegreen; color: white; line-height: 100px; } .room>span{ width: 100%; left: 80px; color: white; } canvas{ /*display: none;*/ } @keyframes tiao{0%{transform: translateY(0);}50%{transform: translateY(-100%);}100%{transform: translateY(0);}} @-webkit-keyframes tiao{0%{transform: translateY(0);}50%{transform: translateY(-100%);}100%{transform: translateY(0);}} .tiao{ animation: tiao 1s ease-in-out .1s; -webkit-animation: tiao 1s ease-in-out .1s; } </style> </head> <body> <div class="room"> <video autoplay width="320" height="240"></video> <div class="beizi">水杯</div> <span>您能夠嘗試敲打桌面</span> </div> <div class="room" style="display: none;"> <canvas width="320" height="240"></canvas> <span>每幀截取</span> </div> <div class="room"> <canvas width="320" height="240"></canvas> <span>上一幀與這一幀的對比差別</span> </div> <div class="room"> <canvas width="320" height="240"></canvas> <span>檢測到動了必定幅度的截圖</span> </div> <script> var w = 320, h = 240; var video = document.querySelector('video'), beizi = document.querySelector(".beizi"), canvas = document.querySelectorAll('canvas'), canvasForDiff = canvas[1]; canvasPhoto = canvas[2]; canvas = canvas[0]; navigator.getUserMedia || navigator.webkitGetUserMedia// || navigator.mozGetUserMedia//ie chrome firefox ({video:true}, function(stream) { video.src = window.URL.createObjectURL(stream); video.play(); }, function(err) { alert('出錯: ' + err) }); //canvas var context = canvas.getContext('2d'), diffCtx = canvasForDiff.getContext('2d'), photo = canvasPhoto.getContext('2d'); //將第二個畫布混合模式設爲「差別」 diffCtx.globalCompositeOperation = 'difference'; var preFrame, //前一幀 curFrame; //當前幀 var diffFrame; //存放差別幀的imageData //捕獲並保存幀內容 function captureAndSaveFrame(){ preFrame = curFrame; context.drawImage(video, 0, 0, w, h); curFrame = canvas.toDataURL(); //轉爲base64並保存 } //繪製base64圖像到畫布上 function drawImg(src, ctx){ ctx = ctx || diffCtx; var img = new Image(); img.src = src; ctx.drawImage(img, 0, 0, w, h); } //渲染先後兩幀差別 function renderDiff(){ if(!preFrame || !curFrame) return; diffCtx.clearRect(0, 0, w, h); drawImg(preFrame); drawImg(curFrame); diffFrame = diffCtx.getImageData( 0, 0, w, h ); //捕獲差別幀的imageData對象 } //計算差別 function calcDiff(){ if(!diffFrame) return 0; var cache = arguments.callee, count = 0; cache.total = cache.total || 0; //整個畫布都是白色時全部像素的值的總和 for (var i = 0, l = diffFrame.width * diffFrame.height * 4; i < l; i += 4) { count += diffFrame.data[i] + diffFrame.data[i + 1] + diffFrame.data[i + 2]; if(!cache.isLoopEver){ //只需在第一次循環裏執行 cache.total += 255 * 3; //單個白色像素值 } } cache.isLoopEver = true; count *= 3; //亮度放大 //返回「差別畫布高亮部分像素總值」佔「畫布全亮狀況像素總值」的比例 return Number(count/cache.total).toFixed(2); } var t,d; //定時捕獲 function timer(delta){ d = 0; setTimeout(function(){ captureAndSaveFrame(); renderDiff(); setTimeout(function(){ if((d=calcDiff())>=0.13){ handel(); } }, 16.7); timer(delta) }, delta || 500); } function handel(){ if(t){return} photo.drawImage(video, 0, 0, w, h); beizi.classList.add('tiao'); canvasForDiff.classList.add('tiao'); document.body.style.backgroundColor = (function(){ return ['red','yellow','black','blue','green','white'][Math.floor(Math.random()*6)]; }()); t = setTimeout(function(){ canvasForDiff.classList.remove('tiao'); beizi.classList.remove('tiao'); t = null; },1000); } timer(); </script> </body> </html>
程序猿們怎麼可能就地止步,讓咱們運用代碼,本身來寫一個自動拍照吧!canvas
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <style> div{ text-align: center; width: 250px; margin: 10px 0; } </style> <body> <video></video> <div> <button onclick="paipaipai()">手動拍照</button> <button onclick="auto()">自動拍照</button> <button onclick="debug()">調試模式</button> </div> <img/> <script src="av.js"></script> <script> var img = document.querySelector("img"); var av = Av({ //style: { // width: 320,//視頻 畫布 寬 // height: 240//高 //}, //deg: 0.12,//靈敏度 觸發動做幅度 //die: 500,//dwon機時間,觸發事件後500ms內不觸發 //delta:300,//取幀間隔 300ms 獲取一次視頻幀 //sw:true//開關,默認爲開 fn:function(data){//觸發函數 img.src = data; } }); function paipaipai(){ av.switchAV(false); img.src = av.getCurFrame(); } function auto(){ av.switchAV(true); } function debug(){ [].map.call(document.querySelectorAll("canvas"),function(c){ c.style.display = 'inline'; }); } </script> </body> </html>
(function() { var av = function(option) { option = option || {}; this.op = (function(o) { for(var i in option) { o[i] = option[i]; } return o; }({ style: { width: 320,//視頻 畫布 寬 height: 240//高 }, deg: 0.12,//靈敏度 觸發動做幅度 die: 500,//dwon機時間,觸發事件後500ms內不觸發 delta:300,//取幀間隔 300ms 獲取一次視頻幀 sw:true//開關,默認爲開 })); this.initEm(); this.initMedia(); this.initCanvas(); this.switchAV(); } var avpro = av.prototype; //初始化基礎元素 avpro.initEm = function() { //video元素 this.video = document.querySelector(this.op.el || 'video'); this.video.setAttribute('autoplay', ''); this.video.style.objectFit = 'fill'; //初始化canvas元素 var canvas = document.createElement("canvas"); canvas.style.display = 'none'; canvas.style.backgroundColor = this.video.style.backgroundColor = 'grey'; canvas.style.width = this.video.style.width = (this.w = canvas.width = this.op.style.width) + 'px'; canvas.style.height = this.video.style.height = (this.h = canvas.height = this.op.style.height) + 'px'; //動做畫布克隆,映射視頻 ac 動做Action 便於記憶 var acCanvas = canvas.cloneNode(true); //對比畫布克隆,比較差別 bw 表示黑白 便於記憶 var bwCanvas = canvas.cloneNode(true); //清除原體釋放資源 canvas = null; //添加至頁面 document.body.appendChild(acCanvas); document.body.appendChild(bwCanvas); this.canvas = acCanvas; this.acCanvas = acCanvas.getContext('2d'); this.bwCanvas = bwCanvas.getContext('2d'); } //初始化攝像頭 avpro.initMedia = function() { var tv = this.video; navigator.getUserMedia || navigator.webkitGetUserMedia //ie chrome ({ video: true }, function(se) { tv.src = window.URL.createObjectURL(se); tv.play(); }, function(err) { console.log('err:' + err); }); } //初始化畫布,幀數 avpro.initCanvas = function() { //將第二個畫布混合模式設爲「差別」 this.bwCanvas.globalCompositeOperation = 'difference'; // 前一幀 當前幀 差別幀 this.preFrame = this.curFrame = this.diffFrame = null; } //開始AV avpro.startAv = function() { var tv = this; var call = arguments.callee; tv.zt = setTimeout(function() { tv.avSaveFrame(); tv.renderDiff(); setTimeout(function() { if(tv.calcDiff() >= tv.op.deg) { //觸發事件 tv.handel(); } }, 16.7); call.call(tv); },tv.op.delta); } //設置開關 和 回調函數 avpro.switchAV = function(sw,fn){ if(sw == undefined ? this.op.sw:sw){ this.startAv(); }else{ this.stopAv(); } fn && (this.op.fn = fn); } avpro.stopAv = function(){ this.zt && clearTimeout(this.zt); } //觸發事件 avpro.handel = function() { var tv = this; if(tv.t) { return; } console.log(tv.fn); tv.op.fn && tv.fn(tv.curFrame); tv.t = setTimeout(function() { tv.t = null; },tv.op.die); } avpro.getCurFrame = function(){ return this.curFrame; } //捕獲並保存幀 avpro.avSaveFrame = function() { //幀替換 this.preFrame = this.curFrame; this.acCanvas.drawImage(this.video, 0, 0, this.w, this.h); //轉爲base64並保存當前幀 this.curFrame = this.canvas.toDataURL(); } //繪製base64圖像到畫布上 avpro.drawImg = function(src, ctx) { ctx = ctx || this.bwCanvas; var img = new Image(); img.src = src; ctx.drawImage(img, 0, 0 ,this.w, this.h); } //渲染先後兩幀差別 avpro.renderDiff = function() { if(!this.preFrame || !this.curFrame) return; this.bwCanvas.clearRect(0, 0, this.w, this.h); this.drawImg(this.preFrame); this.drawImg(this.curFrame); //捕獲差別幀的imageData對象 this.diffFrame = this.bwCanvas.getImageData(0, 0, this.w, this.h); } //計算差別 avpro.calcDiff = function() { if(!this.diffFrame) return 0; var cache = arguments.callee, count = 0; cache.total = cache.total || 0; //整個畫布都是白色時全部像素的值的總和 for(var i = 0, l = this.diffFrame.width * this.diffFrame.height * 4; i < l; i += 4) { count += this.diffFrame.data[i] + this.diffFrame.data[i + 1] + this.diffFrame.data[i + 2]; if(!cache.isLoopEver) { //只需在第一次循環裏執行 cache.total += 255 * 3; //單個白色像素值 } } cache.isLoopEver = true; count *= 3; //亮度放大 //返回「差別畫布高亮部分像素總值」佔「畫布全亮狀況像素總值」的比例 return Number(count / cache.total).toFixed(2); } var nav = null; window.Av = function(op) { return nav || (nav = new av(op)); }; }())
寫前聖成佛:感受本身立刻要寫出一篇無與倫比的文章。
寫後呆成魔:寫的跟流水帳樣的。瀏覽器
最後但願寶強可以好起來。安全