JS实现滑动拼图验证功能完整示例


Posted in Javascript onMarch 29, 2020

本文实例讲述了JS实现滑动拼图验证功能。分享给大家供大家参考,具体如下:

先看一下效果图:

JS实现滑动拼图验证功能完整示例

JS实现滑动拼图验证功能完整示例

JS实现滑动拼图验证功能完整示例

设置画布滑块属性

const l = 42, // 滑块边长
 r = 10, // 滑块半径
 w = 310, // canvas宽度
 h = 155, // canvas高度
 PI = Math.PI
const L = l + r * 2 // 滑块实际边长

设置背景图片:

图片链接地址可以自行更换

function getRandomImg() {
 return 'https://picsum.photos/300/150/?image=' + getRandomNumberByRange(0, 100)
}

CSS部分代码:

.container {
 width: 310px;
 margin: 100px auto;
}
#msg {
 width: 100%;
 line-height: 40px;
 font-size: 14px;
 text-align: center;
}
a:link,
a:visited,
a:hover,
a:active {
 margin-left: 100px;
 color: #0366D6;
}
.block {
 position: absolute;
 left: 0;
 top: 0;
}
.sliderContainer {
 position: relative;
 text-align: center;
 width: 310px;
 height: 40px;
 line-height: 40px;
 margin-top: 15px;
 background: #f7f9fa;
 color: #45494c;
 border: 1px solid #e4e7eb;
}
.sliderContainer_active .slider {
 height: 38px;
 top: -1px;
 border: 1px solid #1991FA;
}
.sliderContainer_active .sliderMask {
 height: 38px;
 border-width: 1px;
}
.sliderContainer_success .slider {
 height: 38px;
 top: -1px;
 border: 1px solid #52CCBA;
 background-color: #52CCBA !important;
}
.sliderContainer_success .sliderMask {
 height: 38px;
 border: 1px solid #52CCBA;
 background-color: #D2F4EF;
}
.sliderContainer_success .sliderIcon {
 background-position: 0 0 !important;
}
.sliderContainer_fail .slider {
 height: 38px;
 top: -1px;
 border: 1px solid #f57a7a;
 background-color: #f57a7a !important;
}
.sliderContainer_fail .sliderMask {
 height: 38px;
 border: 1px solid #f57a7a;
 background-color: #fce1e1;
}
.sliderContainer_fail .sliderIcon {
 background-position: 0 -83px !important;
}
.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;
}
.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;
}
.slider:hover {
 background: #1991FA;
}
.slider:hover .sliderIcon {
 background-position: 0 -13px;
}
.sliderIcon {
 position: absolute;
 top: 15px;
 left: 13px;
 width: 14px;
 height: 10px;
 background: url(img/tb.png) 0 -26px;
 background-size: 34px 471px;
}
.refreshIcon {
 position: absolute;
 right: 0;
 top: 0;
 width: 34px;
 height: 34px;
 cursor: pointer;
 background: url(img/tb.png) 0 -437px;
 background-size: 34px 471px;
}

页面,页面只用放一个div就可以了

<div class="container">
 <div id="captcha" style="position: relative"></div>
</div>

js部分代码,包括验证是否正确

<script type="text/javascript">
 (function(window) {
 const l = 42, // 滑块边长
  r = 10, // 滑块半径
  w = 310, // canvas宽度
  h = 155, // canvas高度
  PI = Math.PI
 const L = l + r * 2 // 滑块实际边长
 function getRandomNumberByRange(start, end) {
  return Math.round(Math.random() * (end - start) + start)
 }
 function createCanvas(width, height) {
  const canvas = createElement('canvas')
  canvas.width = width
  canvas.height = height
  return canvas
 }
 function createImg(onload) {
  const img = createElement('img')
  img.crossOrigin = "Anonymous"
  img.onload = onload
  img.onerror = () => {
  img.src = getRandomImg()
  }
  img.src = getRandomImg()
  return img
 }
 function createElement(tagName) {
  return document.createElement(tagName)
 }
 function addClass(tag, className) {
  tag.classList.add(className)
 }
 function removeClass(tag, className) {
  tag.classList.remove(className)
 }
 function getRandomImg() {
  return 'https://picsum.photos/300/150/?image=' + getRandomNumberByRange(0, 100)
 }
 function draw(ctx, operation, x, y) {
  ctx.beginPath()
  ctx.moveTo(x, y)
  ctx.lineTo(x + l / 2, y)
  ctx.arc(x + l / 2, y - r + 2, r, 0, 2 * PI)
  ctx.lineTo(x + l / 2, y)
  ctx.lineTo(x + l, y)
  ctx.lineTo(x + l, y + l / 2)
  ctx.arc(x + l + r - 2, y + l / 2, r, 0, 2 * PI)
  ctx.lineTo(x + l, y + l / 2)
  ctx.lineTo(x + l, y + l)
  ctx.lineTo(x, y + l)
  ctx.lineTo(x, y)
  ctx.fillStyle = '#fff'
  ctx[operation]()
  ctx.beginPath()
  ctx.arc(x, y + l / 2, r, 1.5 * PI, 0.5 * PI)
  ctx.globalCompositeOperation = "xor"
  ctx.fill()
 }
 function sum(x, y) {
  return x + y
 }
 function square(x) {
  return x * x
 }
 class jigsaw {
  constructor(el, success, fail) {
  this.el = el
  this.success = success
  this.fail = fail
  }
  init() {
  this.initDOM()
  this.initImg()
  this.draw()
  this.bindEvents()
  }
  initDOM() {
  const canvas = createCanvas(w, h) // 画布
  const block = canvas.cloneNode(true) // 滑块
  const sliderContainer = createElement('div')
  const refreshIcon = createElement('div')
  const sliderMask = createElement('div')
  const slider = createElement('div')
  const sliderIcon = createElement('span')
  const text = createElement('span')
  block.className = 'block'
  sliderContainer.className = 'sliderContainer'
  refreshIcon.className = 'refreshIcon'
  sliderMask.className = 'sliderMask'
  slider.className = 'slider'
  sliderIcon.className = 'sliderIcon'
  text.innerHTML = '向右滑动滑块填充拼图'
  text.className = 'sliderText'
  const el = this.el
  el.appendChild(canvas)
  el.appendChild(refreshIcon)
  el.appendChild(block)
  slider.appendChild(sliderIcon)
  sliderMask.appendChild(slider)
  sliderContainer.appendChild(sliderMask)
  sliderContainer.appendChild(text)
  el.appendChild(sliderContainer)
  Object.assign(this, {
   canvas,
   block,
   sliderContainer,
   refreshIcon,
   slider,
   sliderMask,
   sliderIcon,
   text,
   canvasCtx: canvas.getContext('2d'),
   blockCtx: block.getContext('2d')
  })
  }
  initImg() {
  const img = createImg(() => {
   this.canvasCtx.drawImage(img, 0, 0, w, h)
   this.blockCtx.drawImage(img, 0, 0, w, h)
   const y = this.y - r * 2 + 2
   const ImageData = this.blockCtx.getImageData(this.x, y, L, L)
   this.block.width = L
   this.blockCtx.putImageData(ImageData, 0, y)
  })
  this.img = img
  }
  draw() {
  // 随机创建滑块的位置
  this.x = getRandomNumberByRange(L + 10, w - (L + 10))
  this.y = getRandomNumberByRange(10 + r * 2, h - (L + 10))
  draw(this.canvasCtx, 'fill', this.x, this.y)
  draw(this.blockCtx, 'clip', this.x, this.y)
  }
  clean() {
  this.canvasCtx.clearRect(0, 0, w, h)
  this.blockCtx.clearRect(0, 0, w, h)
  this.block.width = w
  }
  bindEvents() {
  this.el.onselectstart = () => false
  this.refreshIcon.onclick = () => {
   this.reset()
  }
  let originX, originY, trail = [],
   isMouseDown = false
  this.slider.addEventListener('mousedown', function(e) {
   originX = e.x, originY = e.y
   isMouseDown = true
  })
  document.addEventListener('mousemove', (e) => {
   if(!isMouseDown) return false
   const moveX = e.x - originX
   const moveY = e.y - originY
   if(moveX < 0 || moveX + 38 >= w) return false
   this.slider.style.left = moveX + 'px'
   var blockLeft = (w - 40 - 20) / (w - 40) * moveX
   this.block.style.left = blockLeft + 'px'
   addClass(this.sliderContainer, 'sliderContainer_active')
   this.sliderMask.style.width = moveX + 'px'
   trail.push(moveY)
  })
  document.addEventListener('mouseup', (e) => {
   if(!isMouseDown) return false
   isMouseDown = false
   if(e.x == originX) return false
   removeClass(this.sliderContainer, 'sliderContainer_active')
   this.trail = trail
   const {
   spliced,
   TuringTest
   } = this.verify()
   if(spliced) {
   if(TuringTest) {
    addClass(this.sliderContainer, 'sliderContainer_success')
    this.success && this.success()
   } else {
    addClass(this.sliderContainer, 'sliderContainer_fail')
    this.text.innerHTML = '再试一次'
    this.reset()
   }
   } else {
   alert("验证失败");
   addClass(this.sliderContainer, 'sliderContainer_fail')
   this.fail && this.fail();
                        //验证失败后,1秒后重新加载图片
   setTimeout(() => {
    this.reset()
   }, 1000)
   }
  })
  }
  verify() {
  const arr = this.trail // 拖动时y轴的移动距离
  const average = arr.reduce(sum) / arr.length // 平均值
  const deviations = arr.map(x => x - average) // 偏差数组
  const stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length) // 标准差
  const left = parseInt(this.block.style.left)
  return {
   spliced: Math.abs(left - this.x) < 10,
   TuringTest: average !== stddev, // 只是简单的验证拖动轨迹,相等时一般为0,表示可能非人为操作
  }
  }
  reset() {
  this.sliderContainer.className = 'sliderContainer'
  this.slider.style.left = 0
  this.block.style.left = 0
  this.sliderMask.style.width = 0
  this.clean()
  this.img.src = getRandomImg()
  this.draw()
  }
 }
 window.jigsaw = {
  init: function(element, success, fail) {
  new jigsaw(element, success, fail).init()
  }
 }
 }(window))
 jigsaw.init(document.getElementById('captcha'), function() {
 alert("验证成功");
 })
</script>

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
Extjs TriggerField在弹出窗口显示不出问题的解决方法
Jan 08 Javascript
JQuery文字列表向上滚动的代码
Nov 13 Javascript
JS获取各种宽度、高度的简单介绍
Dec 19 Javascript
JavaScript比较两个对象是否相等的方法
Feb 06 Javascript
mvc 、bootstrap 结合分布式图简单实现分页
Oct 10 Javascript
微信小程序 列表的上拉加载和下拉刷新的实现
Apr 01 Javascript
vue组件编写之todolist组件实例详解
Jan 22 Javascript
Vue组件和Route的生命周期实例详解
Feb 10 Javascript
解决angularjs前后端分离调用接口传递中文时中文乱码的问题
Aug 13 Javascript
react同构实践之实现自己的同构模板
Mar 13 Javascript
js 计数排序的实现示例(升级版)
Jan 12 Javascript
JavaScript定时器使用方法详解
Mar 26 Javascript
json_decode 索引为数字时自动排序问题解决方法
Mar 28 #Javascript
JS中FormData类实现文件上传
Mar 27 #Javascript
JS中FileReader类实现文件上传及时预览功能
Mar 27 #Javascript
js、jquery实现列表模糊搜索功能过程解析
Mar 27 #jQuery
开发Node CLI构建微信小程序脚手架的示例
Mar 27 #Javascript
微信小程序间使用navigator跳转传值问题实例分析
Mar 27 #Javascript
vue跳转页面的几种方法(推荐)
Mar 26 #Javascript
You might like
计算2000年01月01日起到指定日的天数
2006/10/09 PHP
PHP+SQL 注入攻击的技术实现以及预防办法
2010/12/29 PHP
PHP中使用curl伪造IP的简单方法
2015/08/07 PHP
PHP中两个float(浮点数)比较实例分析
2015/09/27 PHP
PHP对象实例化单例方法
2017/01/19 PHP
PHP连接MySQL数据库三种实现方法
2020/12/10 PHP
javascript背投广告代码的完善
2008/04/08 Javascript
打造基于jQuery的高性能TreeView(asp.net)
2011/02/23 Javascript
js 实现在离开页面时提醒未保存的信息(减少用户重复操作)
2013/01/16 Javascript
Javascript控制页面链接在新窗口打开具体方法
2013/08/16 Javascript
前台js调用后台方法示例
2013/12/02 Javascript
浅谈轻量级js模板引擎simplite
2015/02/13 Javascript
jquery超简单实现手风琴效果的方法
2015/06/05 Javascript
jquery实现超简洁的TAB选项卡效果代码
2015/08/28 Javascript
JQuery标签页效果的两个实例讲解(4)
2015/09/17 Javascript
jQuery查找节点并获取节点属性的方法
2016/09/09 Javascript
前端构建工具之gulp的语法教程
2017/06/12 Javascript
js求数组中全部数字可拼接出的最大整数示例代码
2017/08/25 Javascript
axios对请求各种异常情况处理的封装方法
2018/09/25 Javascript
详解NodeJS Https HSM双向认证实现
2019/03/12 NodeJs
vue悬浮可拖拽悬浮按钮的实例代码
2019/08/20 Javascript
在Python的Flask框架中实现单元测试的教程
2015/04/20 Python
用Python登录好友QQ空间点赞的示例代码
2017/11/04 Python
Python Unittest根据不同测试环境跳过用例的方法
2018/12/16 Python
Python实现的IP端口扫描工具类示例
2019/02/15 Python
Django实现图片上传功能步骤解析
2020/04/22 Python
马克华菲官方商城:Mark Fairwhale
2016/09/04 全球购物
印尼最大的网上书店:Gramedia.com
2018/09/13 全球购物
The Body Shop美体小铺西班牙官网:天然化妆品
2019/06/21 全球购物
后备干部培训方案
2014/05/22 职场文书
励志演讲稿大全
2014/08/21 职场文书
机械工程及自动化专业求职信
2014/09/03 职场文书
表扬稿范文
2015/01/17 职场文书
医务人员岗前培训心得体会
2016/01/08 职场文书
win10怎么设置右下角图标不折叠?Win10设置右下角图标不折叠的方法
2022/07/15 数码科技
Spring Boot优化后启动速度快到飞起技巧示例
2022/07/23 Java/Android