html+jQuery实现拖动滑块图片拼图验证码插件【移动端适用】
程序员文章站
2022-05-25 10:54:22
电脑和手机移动端都适用的jquery拖动滑块图片拼图验证码插件,通过鼠标拖动或触屏滑动填充拼图来进行安全验证,点击刷新可以更换当前待验证的图片。
html & css:...
电脑和手机移动端都适用的jquery拖动滑块图片拼图验证码插件,通过鼠标拖动或触屏滑动填充拼图来进行安全验证,点击刷新可以更换当前待验证的图片。
html & css:
<!doctype html> <html lang="zh"> <head> <meta charset="utf-8"> <meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>拖动滑块图片拼图验证码插件</title> <!--框架样式--> <link href="css/bootstrap.min.css" rel="external nofollow" rel="stylesheet"> <!--图标样式--> <link href="https://cdn.bootcss.com/font-awesome/5.7.2/css/all.min.css" rel="stylesheet"> <!--主要样式--> <link href="disk/slidercaptcha.css" rel="external nofollow" rel="stylesheet" /> <style> .slidercaptcha { margin: 0 auto; width: 314px; height: 286px; border-radius: 4px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.125); margin-top: 40px; } .slidercaptcha .card-body { padding: 1rem; } .slidercaptcha canvas:first-child { border-radius: 4px; border: 1px solid #e6e8eb; } .slidercaptcha.card .card-header { background-image: none; background-color: rgba(0, 0, 0, 0.03); } .refreshicon { top: -54px; } </style> </head> <body> <div class="container-fluid"> <div class="form-row"> <div class="col-12"> <div class="slidercaptcha card"> <div class="card-header"> <span>请完成安全验证</span> </div> <div class="card-body"><div id="captcha"></div></div> </div> </div> </div> </div> <script src="js/jquery-1.11.0.min.js" type="text/javascript"></script> <script src="disk/longbow.slidercaptcha.js"></script> <script> $('#captcha').slidercaptcha({ repeaticon: 'fa fa-redo', setsrc: function () { return 'http://images.sdgxgz.com/pic' + math.round(math.random() * 136) + '.jpg'; }, onsuccess: function () { alert('验证通过!'); } }); </script> </body> </html> disk/slidercaptcha.css: body { overflow-x: hidden; } .block { position: absolute; left: 0; top: 0; } .slidercontainer { position: relative; text-align: center; line-height: 40px; background: #f7f9fa; color: #45494c; border-radius: 2px; } .sliderbg { position: absolute; left: 0; right: 0; top: 0; background-color: #f7f9fa; height: 40px; border-radius: 2px; border: 1px solid #e6e8eb; } .slidercontainer_active .slider { top: -1px; border: 1px solid #1991fa; } .slidercontainer_active .slidermask { border-width: 1px 0 1px 1px; } .slidercontainer_success .slider { top: -1px; border: 1px solid #52ccba; background-color: #52ccba !important; } .slidercontainer_success .slidermask { border: 1px solid #52ccba; border-width: 1px 0 1px 1px; background-color: #d2f4ef; } .slidercontainer_success .slidericon:before { content: "\f00c"; } .slidercontainer_fail .slider { top: -1px; border: 1px solid #f57a7a; background-color: #f57a7a !important; } .slidercontainer_fail .slidermask { border: 1px solid #f57a7a; background-color: #fce1e1; border-width: 1px 0 1px 1px; } .slidercontainer_fail .slidericon:before { content: "\f00d"; } .slidercontainer_active .slidertext, .slidercontainer_success .slidertext, .slidercontainer_fail .slidertext { display: none; } .slidermask { position: absolute; left: 0; top: 0; height: 40px; border: 0 solid #1991fa; background: #d1e9fe; border-radius: 2px; } .slider { position: absolute; top: 0; left: 0; width: 40px; height: 40px; background: #fff; box-shadow: 0 0 3px rgba(0, 0, 0, 0.3); cursor: pointer; transition: background .2s linear; border-radius: 2px; display: flex; align-items: center; justify-content: center; } .slider:hover { background: #1991fa; } .slider:hover .slidericon { background-position: 0 -13px; } .slidertext { position: relative; } .slidericon { } .refreshicon { position: absolute; right: 0; top: 0; cursor: pointer; margin: 6px; color: rgba(0,0,0,.25); font-size: 1rem; z-index: 5; transition: color .3s linear; } .refreshicon:hover { color: #6c757d; } disk/longbow.slidercaptcha.js: (function ($) { 'use strict'; var slidercaptcha = function (element, options) { this.$element = $(element); this.options = $.extend({}, slidercaptcha.defaults, options); this.$element.css({ 'position': 'relative', 'width': this.options.width + 'px', 'margin': '0 auto' }); this.init(); }; slidercaptcha.version = '1.0'; slidercaptcha.author = 'argo@163.com'; slidercaptcha.defaults = { width: 280, // canvas宽度 height: 155, // canvas高度 pi: math.pi, sliderl: 42, // 滑块边长 sliderr: 9, // 滑块半径 offset: 5, // 容错偏差 loadingtext: '正在加载中...', failedtext: '再试一次', bartext: '向右滑动填充拼图', repeaticon: 'fa fa-repeat', maxloadcount: 3, localimages: function () { return 'images/pic' + math.round(math.random() * 4) + '.jpg'; } }; function plugin(option) { return this.each(function () { var $this = $(this); var data = $this.data('lgb.slidercaptcha'); var options = typeof option === 'object' && option; if (data && !/reset/.test(option)) return; if (!data) $this.data('lgb.slidercaptcha', data = new slidercaptcha(this, options)); if (typeof option === 'string') data[option](); }); } $.fn.slidercaptcha = plugin; $.fn.slidercaptcha.constructor = slidercaptcha; var _proto = slidercaptcha.prototype; _proto.init = function () { this.initdom(); this.initimg(); this.bindevents(); }; _proto.initdom = function () { var createelement = function (tagname, classname) { var elment = document.createelement(tagname); elment.classname = classname; return elment; }; var createcanvas = function (width, height) { var canvas = document.createelement('canvas'); canvas.width = width; canvas.height = height; return canvas; }; var canvas = createcanvas(this.options.width - 2, this.options.height) // 画布 var block = canvas.clonenode(true) // 滑块 var slidercontainer = createelement('div', 'slidercontainer'); var refreshicon = createelement('i', 'refreshicon ' + this.options.repeaticon); var slidermask = createelement('div', 'slidermask'); var sliderbg = createelement('div', 'sliderbg'); var slider = createelement('div', 'slider'); var slidericon = createelement('i', 'fa fa-arrow-right slidericon'); var text = createelement('span', 'slidertext'); block.classname = 'block' text.innerhtml = this.options.bartext; var el = this.$element; el.append($(canvas)); el.append($(refreshicon)); el.append($(block)); slider.appendchild(slidericon); slidermask.appendchild(slider); slidercontainer.appendchild(sliderbg); slidercontainer.appendchild(slidermask); slidercontainer.appendchild(text); el.append($(slidercontainer)); object.assign(this, { canvas, block, slidercontainer: $(slidercontainer), refreshicon, slider, slidermask, slidericon, text: $(text), canvasctx: canvas.getcontext('2d'), blockctx: block.getcontext('2d') }) }; _proto.initimg = function () { var that = this; var isie = window.navigator.useragent.indexof('trident') > -1; var l = this.options.sliderl + this.options.sliderr * 2 + 3; // 滑块实际边长 var drawimg = function (ctx, operation) { var l = that.options.sliderl; var r = that.options.sliderr; var pi = that.options.pi; var x = that.x; var y = that.y; ctx.beginpath() ctx.moveto(x, y) ctx.arc(x + l / 2, y - r + 2, r, 0.72 * pi, 2.26 * pi) ctx.lineto(x + l, y) ctx.arc(x + l + r - 2, y + l / 2, r, 1.21 * pi, 2.78 * pi) ctx.lineto(x + l, y + l) ctx.lineto(x, y + l) ctx.arc(x + r - 2, y + l / 2, r + 0.4, 2.76 * pi, 1.24 * pi, true) ctx.lineto(x, y) ctx.linewidth = 2 ctx.fillstyle = 'rgba(255, 255, 255, 0.7)' ctx.strokestyle = 'rgba(255, 255, 255, 0.7)' ctx.stroke() ctx[operation]() ctx.globalcompositeoperation = isie ? 'xor' : 'overlay' } var getrandomnumberbyrange = function (start, end) { return math.round(math.random() * (end - start) + start); }; var img = new image(); img.crossorigin = "anonymous"; var loadcount = 0; img.onload = function () { // 随机创建滑块的位置 that.x = getrandomnumberbyrange(l + 10, that.options.width - (l + 10)); that.y = getrandomnumberbyrange(10 + that.options.sliderr * 2, that.options.height - (l + 10)); drawimg(that.canvasctx, 'fill'); drawimg(that.blockctx, 'clip'); that.canvasctx.drawimage(img, 0, 0, that.options.width - 2, that.options.height); that.blockctx.drawimage(img, 0, 0, that.options.width - 2, that.options.height); var y = that.y - that.options.sliderr * 2 - 1; var imagedata = that.blockctx.getimagedata(that.x - 3, y, l, l); that.block.width = l; that.blockctx.putimagedata(imagedata, 0, y); that.text.text(that.text.attr('data-text')); }; img.onerror = function () { loadcount++; if (window.location.protocol === 'file:') { loadcount = that.options.maxloadcount; console.error("can't load pic resource file from file protocal. please try http or https"); } if (loadcount >= that.options.maxloadcount) { that.text.text('加载失败').addclass('text-danger'); return; } img.src = that.options.localimages(); }; img.setsrc = function () { var src = ''; loadcount = 0; that.text.removeclass('text-danger'); if ($.isfunction(that.options.setsrc)) src = that.options.setsrc(); if (!src || src === '') src = 'https://picsum.photos/' + that.options.width + '/' + that.options.height + '/?image=' + math.round(math.random() * 20); if (isie) { // ie浏览器无法通过img.crossorigin跨域,使用ajax获取图片blob然后转为dataurl显示 var xhr = new xmlhttprequest() xhr.onloadend = function (e) { var file = new filereader(); // filereader仅支持ie10+ file.readasdataurl(e.target.response); file.onloadend = function (e) { img.src = e.target.result; } } xhr.open('get', src); xhr.responsetype = 'blob'; xhr.send(); } else img.src = src; }; img.setsrc(); this.text.attr('data-text', this.options.bartext); this.text.text(this.options.loadingtext); this.img = img }; _proto.clean = function () { this.canvasctx.clearrect(0, 0, this.options.width, this.options.height); this.blockctx.clearrect(0, 0, this.options.width, this.options.height); this.block.width = this.options.width; }; _proto.bindevents = function () { var that = this; this.$element.on('selectstart', function () { return false; }); $(this.refreshicon).on('click', function () { that.text.text(that.options.bartext); that.reset(); if ($.isfunction(that.options.onrefresh)) that.options.onrefresh.call(that.$element); }); var originx, originy, trail = [], ismousedown = false var handledragstart = function (e) { if (that.text.hasclass('text-danger')) return; originx = e.clientx || e.touches[0].clientx; originy = e.clienty || e.touches[0].clienty; ismousedown = true; }; var handledragmove = function (e) { if (!ismousedown) return false; var eventx = e.clientx || e.touches[0].clientx; var eventy = e.clienty || e.touches[0].clienty; var movex = eventx - originx; var movey = eventy - originy; if (movex < 0 || movex + 40 > that.options.width) return false; that.slider.style.left = (movex - 1) + 'px'; var blockleft = (that.options.width - 40 - 20) / (that.options.width - 40) * movex; that.block.style.left = blockleft + 'px'; that.slidercontainer.addclass('slidercontainer_active'); that.slidermask.style.width = (movex + 4) + 'px'; trail.push(movey); }; var handledragend = function (e) { if (!ismousedown) return false ismousedown = false var eventx = e.clientx || e.changedtouches[0].clientx if (eventx == originx) return false that.slidercontainer.removeclass('slidercontainer_active'); that.trail = trail var { spliced, verified } = that.verify() if (spliced && verified) { that.slidercontainer.addclass('slidercontainer_success'); if ($.isfunction(that.options.onsuccess)) that.options.onsuccess.call(that.$element); } else { that.slidercontainer.addclass('slidercontainer_fail'); if ($.isfunction(that.options.onfail)) that.options.onfail.call(that.$element); settimeout(() => { that.text.text(that.options.failedtext); that.reset(); }, 1000); } }; this.slider.addeventlistener('mousedown', handledragstart); this.slider.addeventlistener('touchstart', handledragstart); document.addeventlistener('mousemove', handledragmove); document.addeventlistener('touchmove', handledragmove); document.addeventlistener('mouseup', handledragend); document.addeventlistener('touchend', handledragend); document.addeventlistener('mousedown', function () { return false; }); document.addeventlistener('touchstart', function () { return false; }); }; _proto.verify = function () { var sum = function (x, y) { return x + y; }; var square = function (x) { return x * x; }; var arr = this.trail // 拖动时y轴的移动距离 var average = arr.reduce(sum) / arr.length; var deviations = arr.map(function (x) { return x - average; }); var stddev = math.sqrt(deviations.map(square).reduce(sum) / arr.length); var left = parseint(this.block.style.left); return { spliced: math.abs(left - this.x) < this.options.offset, verified: stddev !== 0, // 简单验证下拖动轨迹,为零时表示y轴上下没有波动,可能非人为操作 } }; _proto.reset = function () { this.slidercontainer.removeclass('slidercontainer_fail slidercontainer_success'); this.slider.style.left = 0 this.block.style.left = 0 this.slidermask.style.width = 0 this.clean() this.text.attr('data-text', this.text.text()); this.text.text(this.options.loadingtext); this.img.setsrc(); }; })(jquery);
附录1:需要注意css和js的引用路径;
总结
以上所述是小编给大家介绍的html+jquery实现拖动滑块图片拼图验证码插件希望对大家有所帮助
上一篇: 长白头发吃什么能变黑这样吃出一头黑发
下一篇: es6中reduce的基本使用方法