说到防抖,想必多数人首先想到的是相机的防抖。因为我们并不是机器人,所以拿手机拍照的时候,手都会有不易察觉的抖动,这样的抖动会影响相片的质量。手机对这些情况做的一些补偿操作,减小了手抖对成像造成的影响。
我们都知道,JavaScript 是一门编程语言,不是人类也不是机器人。那什么情况下,会产生“抖动”呢?
联想一个平平无奇的登录框,当用户信息输入完毕,点击登录按钮,可能网速有点慢还是啥的,用户等得不耐烦,不停点击,导致鼠标患上帕金森,登录按钮就被一次一次地点击,前端不停地向后台发送重复的请求。
如下面的例子(这里点击一次,执行 console.log('click'), 并且用 console.log('submit') 代指请求):
可以看到,短时间内连续点击,每次点击都会触发请求.
这种情况,就属于“抖动”。
服务器接收到这样的请求,肯定是一脸懵啊,这谁顶得住?
这个时候,就需要像手机相机一样,做一些操作,减少鼠标抖动对网络请求的影响,减轻服务器的压力。
“抖动”情景下,多次点击,导致发送了多次一样的请求。函数防抖的处理方式是:先规定一个时间段,比如一秒,点击按钮,一秒之后再发送请求,假如一秒内又产生了一次点击,那么重新计时,点击过后一秒再发送请求。
这样一来,规定时间段内的快速点击,只会产生一次请求。不管打字多快的手速,也战胜不了防抖的函数。
直接上代码:
const debounce=(func, delay=200)=> { let timeout=null return function () { clearTimeout(timeout) timeout=setTimeout(()=> { func.apply(this, arguments) }, delay) } }
debounce 函数接受一个函数 func 和一个默认为 200 毫秒延迟时间 delay 作为参数。返回一个函数,触发返回的函数,开始计时,delay 毫秒后触发 func, 假如 delay 时间段内,再次触发这个函数,那么重新计时,delay 毫秒后触发 func.
debounce 首先声明变量 timeout, 用于存放之后 setTimeout 函数返回的定时器编号。
然后返回一个函数,函数内执行 clearTimeout 来依据先前声明的 timeout 来清除定时器。当然,一开始,传入的 timeout 值为 null, 这时的清除操作忽略不计。
接着,执行 setTimeout, 在至少 delay 规定的毫秒后,将 setTimeout 的回调函数添加到当前事件队列,回调内执行 func 函数。并且把返回的定时器编号赋值给 timeout , 这样,下一次触发 debounce 返回的函数时,就可以清除通过上面的 clearTimeout(timeout) 来清除定时器 。
注意到上面执行 func 用的是 func.apply(this, arguments), 这样一来,就可以对 debounce 返回的那个函数传递参数,func 执行的时候,再把参数传给 func.
来用一下:
const submit=()=> { console.log('submit') } const debounceSubmit=debounce(submit, 500) let btnSubmit=document.getElementById('submit') btnSubmit.addEventListener('click', ()=> { console.log('click') debounceSubmit() })
这里将 submit 函数传入 debounce 函数,并设置延迟时间为 500 毫秒。 debounce 返回的函数赋给 debounceSubmit , 然后在提交按钮 btnSubmit 的点击事件回调中执行 debounceSubmit.
看下效果:
上图中,一开始的几次连续点击,都不会触发 submit ,停止点击后,才触发了一次 submit . 之后两次有一定间隔时间的点击,都触发了 submit.
函数的防抖将一定时间内的多次操作,减少为一次,去除冗余,节约资源。
去,当我们面对交通违法记分时,往往感到头疼和无奈。不仅是因为扣分带来的经济和时间成本,更是因为缺乏一个系统、便捷的方式来学习和了解交通规则,从而避免再次犯错。然而,现在有了学法减分工具,这一切都变得简单而高效。
这是微信公众号
一款专为驾驶员打造的交通安全学习减分题库,通过减分侠随时在线刷题练习,涵盖丰富的交通安全知识,让学习减分更轻松!
下方附上一些测试的试题及答案
1、【35】
A.other B.big C.little D.robot
答案:C
2、“足智多谋、随机应变”是思维的哪种品质?()
A.广阔性 B.独立性 C.灵活性 D.逻辑性
答案:C
3、【C20】
A.formed B.caused C.made D.fallen
答案:A
4、机上遇到特别肥胖的旅客时,乘务员要主动为该旅客提供____________________。
答案:加长安全带
5、下列哪项不是苏联“休克疗法”的内容?()
A.快速私有化 B.自由化 C.稳定化 D.扩张性财政政策
答案:D
6、【31】
A.keeps B.draws C.inclines D.tends
答案:B
7、Which of the following statements is true?
A.Five percent of Americans are very unhappy. B.Over fifty percent of Americans are very happy. C.Twenty percent of Americans are very unhappy. D.Sixty to seventy percent of Americans are not happy.
答案:A
8、嵐山は日本の___にあります()
A、東京 B、京都 C、奈良
答案:B
9、【C11】
A.hot-sold B.good-sold C.hot-selling D.good-selling
答案:C
10、【36】
A.similarly B.greatly C.hardly D.clearly
答案:C
11、产业结构理论是关于产业内企业结构的理论。
对 错
答案:错
12、【单选题】下列保险项目中只由用人单位缴纳保险费的是() 答案:工伤保险
答案:工伤保险
13、【C9】
A.And B.While C.But D.Nevertheless
答案:A
14、【31】
A.some one B.one C.he D.reader
答案:B
15、【29】
A.fabulous B.frigid C.feeble D.fashionable
答案:D
coursera是面向大学生的免费在线学习APP
它与全世界最顶尖的大学和机构合作,提供任何人可学习的在线课程。
这是一个网站
:题库全面,包含公职、消防、煤矿、国网、医学等行业题目。支持拍照、读屏搜题。简单易用、一键开始、快速响应!
以番茄工作法为基础的时间管理,科学方便。开启学霸模式可以让我们专注于自己做的事情,避免其他娱乐软件的打扰。
这是一款提升我们的自律和专注度用的APP。
这是一个网站
涵盖初高中/大学/专升本/考研/成人自考/各类资格证等等考试题目,同时支持截图搜题、语音搜题,里面还有1000+考试题库可以练习
中国大学MOOC(慕课) 是国内优质的中文MOOC学习平台
由爱课程网携手网易云课堂打造。平台拥有包括985高校在内提供的千余门课程,其中首批获得认定的国家精品在线开放课程322门
菜鸟教程是一个完全免费的编程学习软件。
它免费提供了HTML / CSS 、JavaScript 、服务端、移动端、XML 教程、http://ASP.NET 、Web Service 、开发工具、网站建设;每类教程还细分了很多种不同的教程,例如服务端学习教程包括:PHP 教程、Python 基础教程、Python 3 教程、Linux 教程、Java 教程、Ruby 教程、C 语言教程、C++ 教程、Go 语言教程、正则表达式等丰富的编程学习教程
这是个微信公众号
搜索的题目全部都有详细的提示,以及中间做题步骤、解决方法,你可以导入照片或拍照的方式输入数学问题,它还支持将重点题目进行收集整理,非常方便大家用来复习。
微信读书是腾讯官方出品的一款在线小说阅读APP,它的书库储量非常丰富,我们可以直接通过搜索获取到想要阅读的书籍,喜欢阅读古今中外名著书籍的小伙伴值得尝试。
我们可以将想要阅读的书籍加入书架,还能够设置虚拟书架,这样你的好友就不知道你在读什么书了。
祝愿每一位大学生都能够充分利用这些日常学习工具,成为自己学业道路上的强者。
近刷脸支付很火,老板们当然要追赶时代潮流,于是就有了刷脸支付这个项目。前端实现关键的技术是摄像头录像,拍照和人脸比对,本文来探讨一下如何在html5环境中如何实现刷脸支付以及开发过程中遇到的问题。
html5中获取手机上的图片,有两种方式,使用input,如下可以打开摄像头拍照:
<input type="file" capture="camera" accept="image/*"/>
另外如果想打开相册,可以这样:
<input type="file" accept="img/*">
但是这两种方式都会有兼容性问题,用过的同学可能都知道。
getUserMedia是html5一个新的api,官方一点的定义是:
MediaDevices.getUserMedia() 会提示用户给予使用媒体输入的许可,媒体输入会产生一个MediaStream,里面包含了请求的媒体类型的轨道。此流可以包含一个视频轨道(来自硬件或者虚拟视频源,比如相机、视频采集设备和屏幕共享服务等等)、一个音频轨道(同样来自硬件或虚拟音频源,比如麦克风、A/D转换器等等),也可能是其它轨道类型。
简单一点说就是可以获取到用户摄像头。
同上面input一样,这种方式也有兼容性问题,不过可以使用其他方式解决,这里可以参考MediaDevices.getUserMedia(),文档中有介绍"在旧的浏览器中使用新的API"。我这里在网上也找了一些参考,总结出一个相对全面的getUserMedia版本,代码如下:
// 访问用户媒体设备
getUserMedia(constrains, success, error) {
if (navigator.mediaDevices.getUserMedia) {
//最新标准API
navigator.mediaDevices.getUserMedia(constrains).then(success).catch(error);
} else if (navigator.webkitGetUserMedia) {
//webkit内核浏览器
navigator.webkitGetUserMedia(constrains).then(success).catch(error);
} else if (navigator.mozGetUserMedia) {
//Firefox浏览器
navagator.mozGetUserMedia(constrains).then(success).catch(error);
} else if (navigator.getUserMedia) {
//旧版API
navigator.getUserMedia(constrains).then(success).catch(error);
} else {
this.scanTip="你的浏览器不支持访问用户媒体设备"
}
}
获取设备方法有两个回调函数,一个是成功,一个是失败。成功了就开始播放视频,播放视屏其实就是给video设置一个url,并调用play方法,这里设置url要考虑不同浏览器兼容性,代码如下:
success(stream) {
this.streamIns=stream
// 设置播放地址,webkit内核浏览器
this.URL=window.URL || window.webkitURL
if ("srcObject" in this.$refs.refVideo) {
this.$refs.refVideo.srcObject=stream
} else {
this.$refs.refVideo.src=this.URL.createObjectURL(stream)
}
this.$refs.refVideo.onloadedmetadata=e=> {
// 播放视频
this.$refs.refVideo.play()
this.initTracker()
}
},
error(e) {
this.scanTip="访问用户媒体失败" + e.name + "," + e.message
}
注意:
视屏在video中播放成功之后就开始识别人脸了,这里使用到一个第三方的功能tracking.js,是国外的大神写的JavaScript图像识别插件。关键代码如下:
// 人脸捕捉
initTracker() {
this.context=this.$refs.refCanvas.getContext("2d") // 画布
this.tracker=new tracking.ObjectTracker(['face']) // tracker实例
this.tracker.setStepSize(1.7) // 设置步长
this.tracker.on('track', this.handleTracked) // 绑定监听方法
try {
tracking.track('#video', this.tracker) // 开始追踪
} catch (e) {
this.scanTip="访问用户媒体失败,请重试"
}
}
捕获到人脸之后,可以在页面上用一个小方框标注出来,这样有点交互效果。
// 追踪事件
handleTracked(e) {
if (e.data.length===0) {
this.scanTip='未检测到人脸'
} else {
if (!this.tipFlag) {
this.scanTip='检测成功,正在拍照,请保持不动2秒'
}
// 1秒后拍照,仅拍一次
if (!this.flag) {
this.scanTip='拍照中...'
this.flag=true
this.removePhotoID=setTimeout(()=> {
this.tackPhoto()
this.tipFlag=true
}, 2000)
}
e.data.forEach(this.plot)
}
}
在页面中画一些方框,标识出人脸:
<div class="rect" v-for="item in profile"
:style="{ width: item.width + 'px', height: item.height + 'px', left: item.left + 'px', top: item.top + 'px'}"></div>
// 绘制跟踪框
plot({x, y, width: w, height: h}) {
// 创建框对象
this.profile.push({ width: w, height: h, left: x, top: y })
}
拍照,就是使用video作为图片源,在canvas中保存一张图片下来,注意这里使用toDataURL方法的时候可以设置第二个参数quality,从0到1,0表示图片比较粗糙,但是文件比较小,1表示品质最好。
// 拍照
tackPhoto() {
this.context.drawImage(this.$refs.refVideo, 0, 0, this.screenSize.width, this.screenSize.height)
// 保存为base64格式
this.imgUrl=this.saveAsPNG(this.$refs.refCanvas)
// this.compare(imgUrl)
this.close()
},
// Base64转文件
getBlobBydataURI(dataURI, type) {
var binary=window.atob(dataURI.split(',')[1]);
var array=[];
for(var i=0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {
type: type
});
},
// 保存为png,base64格式图片
saveAsPNG(c) {
return c.toDataURL('image/png', 0.3)
}
拍照完成之后就可以把文件发送给后端,让后端进行对比验证,这里后端使用的是阿里云的接口。
最后,demo我已经放在github上了,感兴趣可以打开看一下。
效果如下:
最后放在项目中,无非就是最后一个步骤,去调用接口比对,根据比对结果成功是成功还是失败,决定是人脸支付还是继续使用原来的密码支付,效果如下:
ps:这里人脸比对失败了,是因为我带着口罩,就不呲牙露脸了。
作者:Tyler Ning 出处:http://www.cnblogs.com/tylerdonet/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,如有问题,可以通过以下邮箱地址344805262@qq.com 联系我,非常感谢。
*请认真填写需求信息,我们会在24小时内与您取得联系。