资讯专栏INFORMATION COLUMN

帧绘玩=>使用video与canvas玩耍的伪“人体感应”

leonardofed / 3381人阅读

摘要:写在前面感谢各位在前端一直努力奋斗的程序猿们,希望以后的会更美好。写后呆成魔写的跟流水账样的。最后希望宝强能够好起来。

写在前面

感谢各位在前端一直努力奋斗的程序猿们,希望以后的HelloWorld会更美好。

感谢来看看机智的前端童鞋怎么防盗作者的分享

感谢停了一天的网终于恢复

干货

先来分享2张图
1)王的愤怒

2) 程序猿的愤怒

原理

通过启用浏览器摄像的方式,把每一帧的图映射到canvas上,通过比较上一帧与当前帧的差异,算出来的差异占的百分比,超过某个百分比就触发函数。

栗子:我摇动我的双手,两个帧的手的位置不一样,从而差异占的比例就挺大的,就触发了写好的回调函数{

给水杯添加一个动画
给body更换一个背景颜色

}
程序猿的愤怒是通过在来看看机智的前端童鞋怎么防盗借鉴的代码实现的,
原作者项目GitHub地址

同时,在不破坏原作者源码的基础下,愤怒的程序猿代码如下:



    
        
        愤怒的程序猿
        
        
    
    
        
水杯
您可以尝试敲打桌面
上一帧与这一帧的对比差异
检测到动了一定幅度的截图

Q:在谷歌浏览器上运行的,表示页面一片空白,啥都没有,请问这是为什么?

A:通过谷歌的文档可以得知,这是为了安全性考虑,非 HTTPS 的服务端请求都不能接入摄像头,简单来说,chrome通过文件的方式打开一个html文件,是无法开启摄像头的,可以在本地服务端打开哦!如:localhost

还要注意写兼容哦!楼主只写了chrome的

嗨起来

程序猿们怎么可能就地止步,让我们运用代码,自己来写一个自动拍照吧!

AV.html



    
        
        
    
    
    
        
        
        

AV.js

(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));
    };

}())
遗言

写前圣成佛:感觉自己马上要写出一篇无与伦比的文章。
写后呆成魔:写的跟流水账样的。

最后希望宝强能够好起来。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/80076.html

相关文章

  • 绘玩=&gt;使用videocanvas玩耍的伪人体感应

    摘要:写在前面感谢各位在前端一直努力奋斗的程序猿们,希望以后的会更美好。写后呆成魔写的跟流水账样的。最后希望宝强能够好起来。 写在前面 感谢各位在前端一直努力奋斗的程序猿们,希望以后的HelloWorld会更美好。 感谢来看看机智的前端童鞋怎么防盗作者的分享 感谢停了一天的网终于恢复 干货 先来分享2张图1)王的愤怒showImg(https://segmentfault.com/img/b...

    winterdawn 评论0 收藏0
  • 绘玩=&gt;使用videocanvas玩耍的伪人体感应

    摘要:写在前面感谢各位在前端一直努力奋斗的程序猿们,希望以后的会更美好。写后呆成魔写的跟流水账样的。最后希望宝强能够好起来。 写在前面 感谢各位在前端一直努力奋斗的程序猿们,希望以后的HelloWorld会更美好。 感谢来看看机智的前端童鞋怎么防盗作者的分享 感谢停了一天的网终于恢复 干货 先来分享2张图1)王的愤怒showImg(https://segmentfault.com/img/b...

    qqlcbb 评论0 收藏0
  • Vue+canvas实现视频截图功能

      上传视频要提供视频封面(视频封面必填),这是在开发中实际问题。封面可以用户自己制作并上传,但这样脱离网站,体验不好,常见的处理方案就是用户未选择或上传封面时,自动截取视频第一帧作为封面,但这样并不友好。因此考虑视频上传后,在播放中由人员自行截取画面作为视频封面。  简单效果如图:  前端代码如下:  <template>   <div>   <videosrc=&...

    3403771864 评论0 收藏0
  • 央视和阿里云爆改一间房,帮视障人群回归正常世界

    摘要:月日,央视秘密大改造节目展示了一项终极挑战,为一位视障人士改造房屋。比起接受精心的照顾,视障人士更愿意接受能让他们回归正常世界的工具。 7月28日,央视《秘密大改造》节目展示了一项终极挑战,为一位视障人士改造房屋。阿里云IoT工程师代立晨志愿参与挑战,他在两周时间里,通过大量的传感设备、网络设置、传输指令,让这间69平米的房子仿佛被赋予生命,它能听会看,可以认识主人、陪伴主人、照顾主人...

    刘永祥 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<