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实现select下拉框获取当前选中文本、值、索引
May 08 jQuery
jQuery Layer弹出层传值到父页面的实现代码
Aug 17 jQuery
简单实现jquery隔行变色
Nov 09 jQuery
jQuery实现鼠标响应式透明度渐变动画效果示例
Feb 13 jQuery
jQuery实现的淡入淡出与滑入滑出效果示例
Apr 18 jQuery
基于jQuery使用Ajax动态执行模糊查询功能
Jul 05 jQuery
jQuery实现表格隔行换色
Sep 01 jQuery
jQuery+Datatables实现表格批量删除功能【推荐】
Oct 24 jQuery
JQuery实现ajax请求的示例和注意事项
Dec 10 jQuery
jQuery利用cookie 实现本地收藏功能(不重复无需多次命名)
Nov 07 jQuery
jQuery实现简单QQ聊天框
Aug 27 jQuery
jQuery编写QQ简易聊天框
Aug 27 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
PHP 数字左侧自动补0
2008/03/31 PHP
php url地址栏传中文乱码解决方法集合
2010/06/25 PHP
PHP访问MYSQL数据库封装类(附函数说明)
2010/12/04 PHP
基于PHP编程注意事项的小结
2013/04/27 PHP
微信公众号点击菜单即可打开并登录微站的实现方法
2014/11/14 PHP
php模拟post提交数据的方法
2015/02/12 PHP
浅谈PHP定义命令空间的几个注意点(推荐)
2016/10/29 PHP
js jquery做的图片连续滚动代码
2008/01/06 Javascript
如何编写高质量JS代码
2014/12/28 Javascript
js同源策略详解
2015/05/21 Javascript
JS获取IE版本号与HTML设置IE文档模式的方法
2016/10/09 Javascript
jQuery实现复制到粘贴板功能
2017/02/11 Javascript
利用ECharts.js画K线图的方法示例
2018/01/10 Javascript
Vue三种常用传值示例(父传子、子传父、非父子)
2018/07/24 Javascript
vue项目打包之后背景样式丢失的解决方案
2019/01/17 Javascript
JS实现集合的交集、补集、差集、去重运算示例【ES5与ES6写法】
2019/02/18 Javascript
JavaScript实现五子棋游戏的方法详解
2019/07/08 Javascript
JavaScript实现拖拽功能
2020/02/11 Javascript
详解JavaScript 事件流
2020/09/02 Javascript
[04:11]2014DOTA2国际邀请赛 CIS遗憾出局梦想不灭
2014/07/09 DOTA
[03:58]2014DOTA2国际邀请赛 龙宝赛后解密DK获胜之道
2014/07/14 DOTA
解读Django框架中的低层次缓存API
2015/07/24 Python
Python数据结构与算法之使用队列解决小猫钓鱼问题
2017/12/14 Python
python 将list转成字符串,中间用符号分隔的方法
2018/10/23 Python
python实现输出一个序列的所有子序列示例
2019/11/18 Python
CSS3中Transform动画属性用法详解
2016/07/04 HTML / CSS
使用css3背景渐变中的透明度来设置不同颜色的背景渐变
2014/03/31 HTML / CSS
HTML5轻松实现全屏视频背景的示例
2018/04/23 HTML / CSS
瑜伽国际:Yoga International
2018/04/18 全球购物
母亲80寿诞答谢词
2014/01/16 职场文书
2014年应急工作总结
2014/12/11 职场文书
银行实习推荐信
2015/03/27 职场文书
男人帮观后感
2015/06/18 职场文书
2015最新婚礼主持词
2015/06/30 职场文书
爱国主题班会教案
2015/08/14 职场文书
python之PySide2安装使用及QT Designer UI设计案例教程
2021/07/26 Python