欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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实现拖动滑块图片拼图验证码插件希望对大家有所帮助