html+jQuery实现拖动滑块图片拼图验证码插件【移动端适用】


Posted in jQuery onSeptember 10, 2019

电脑和手机移动端都适用的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实现拖动滑块图片拼图验证码插件希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

jQuery 相关文章推荐
jQuery输入框密码的显示隐藏【代码分享】
Apr 29 jQuery
jquery实现图片轮播器
May 23 jQuery
最常用的jQuery表单验证(简单)
May 23 jQuery
浅谈struts1 &amp; jquery form 文件异步上传
May 25 jQuery
jQuery动态添加li标签并添加属性和绑定事件方法
Feb 24 jQuery
jQuery滚动条美化插件nicescroll简单用法示例
Apr 18 jQuery
jQuery序列化form表单数据为JSON对象的实现方法
Sep 20 jQuery
学习jQuery中的noConflict()用法
Sep 28 jQuery
jQuery实现弹出层效果
Dec 10 jQuery
jQuery擦除插件eraser使用方法详解
Jan 11 jQuery
jQuery插件simplePagination的使用方法示例
Apr 28 jQuery
jQuery实现简单全选框
Sep 13 jQuery
jQuery实现每日秒杀商品倒计时功能
Sep 06 #jQuery
JS秒杀倒计时功能完整实例【使用jQuery3.1.1】
Sep 03 #jQuery
JavaScript自动生成 年月范围 选择功能完整示例【基于jQuery插件】
Sep 03 #jQuery
Jquery动态列功能完整实例
Aug 30 #jQuery
jQuery - AJAX load() 实例用法详解
Aug 27 #jQuery
jQuery实现判断滚动条滚动到document底部的方法分析
Aug 27 #jQuery
解决jquery validate 验证不通过后验证正确的信息仍残留在label上的方法
Aug 27 #jQuery
You might like
15种PHP Encoder的比较
2007/03/06 PHP
收藏的PHP常用函数 推荐收藏保存
2010/02/21 PHP
PHP ? EasyUI DataGrid 资料取的方式介绍
2012/11/07 PHP
PHP邮箱验证示例教程
2016/06/01 PHP
php好代码风格的阶段性总结
2016/06/25 PHP
利用PHP命令行模式采集股票趋势信息
2016/08/09 PHP
PHP递归实现文件夹的复制、删除、查看大小操作示例
2017/08/11 PHP
php 获取xml接口数据的处理方法
2018/05/31 PHP
让innerHTML的脚本也可以运行起来
2006/07/01 Javascript
腾讯与新浪的通过IP地址获取当前地理位置(省份)的接口
2010/07/26 Javascript
Jquery实现自定义窗口随意的拖拽
2014/03/12 Javascript
js获取url中的参数且参数为中文时通过js解码
2014/03/19 Javascript
深入探密Javascript数组方法
2015/01/08 Javascript
JavaScript插件化开发教程(五)
2015/02/01 Javascript
javascript表格的渲染组件
2015/07/03 Javascript
javaScript基础详解
2017/01/19 Javascript
canvas绘制七巧板
2017/02/03 Javascript
利用Node.js+Koa框架实现前后端交互的方法
2017/02/27 Javascript
详解Vue.js组件可复用性的混合(mixin)方式和自定义指令
2017/09/06 Javascript
ng-alain表单使用方式详解
2018/07/10 Javascript
jQuery实现获取当前鼠标位置并输出功能示例
2019/01/05 jQuery
在微信小程序中使用vant的方法
2019/06/07 Javascript
深入学习JavaScript 高阶函数
2019/06/11 Javascript
javascript设计模式 ? 外观模式原理与用法实例分析
2020/04/15 Javascript
python实现颜色空间转换程序(Tkinter)
2015/12/31 Python
Python编程判断一个正整数是否为素数的方法
2017/04/14 Python
Python中flatten( )函数及函数用法详解
2018/11/02 Python
tensorflow 获取所有variable或tensor的name示例
2020/01/04 Python
Python实现一个简单的毕业生信息管理系统的示例代码
2020/06/08 Python
python virtualenv虚拟环境配置与使用教程详解
2020/07/13 Python
详解pandas赋值失败问题解决
2020/11/29 Python
世界上最大的巴士旅游观光公司:Big Bus Tours
2016/10/20 全球购物
美国新兴城市生活方式零售商:VILLA
2017/12/06 全球购物
萨克斯第五大道英国:Saks Fifth Avenue英国
2019/04/01 全球购物
2016年幼儿园庆六一开幕词
2016/03/04 职场文书
python图片灰度化处理的几种方法
2021/06/23 Python