滑块验证用途很多,本文描述用几张背景图、js代码、css效果,制作一个滑块验证的效果;
原理分析:
采用随机变换背景图,得到不同用户刷新页面,出现的滑块图片背景不同;
采用css+js的html元素拖动效果,实现滑块的左右滑动效果;
局限性: 没法检测高级机器人模拟滑动,比如从滑动速度,滑动的时间上,没有实现控制。现在优秀的开源第三方滑块接口,可以实现一些智能判断;
本效果,暂时未具备这些高级功能。
以下是js代码部分:
/*下面是滑块验证码*/ (function (window) { const l = 42, // 滑块边长 r = 10, // 滑块半径 w = 360, // 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 '/core/css/xficon/slide' + getRandomNumberByRange(0, 10)+ ".jpg" } 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[removed] = '请按住滑块 拖至右边' 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) document.getElementById("slideCodeBar").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; let self = this; var mousedown = function(e){ if(e.targetTouches && e.targetTouches.length==1){ var te = e.targetTouches[0]; e.x = te.clientX; e.y = te.clientY; } originX = e.x, originY = e.y isMouseDown = true; document.getElementById('slideCodeBox').style.display = "block"; } self.slider.addEventListener('mousedown',mousedown ); self.slider.addEventListener('touchstart',mousedown ); /**/ var mousemove = function(e){ if(e.targetTouches && e.targetTouches.length==1){ var te = e.targetTouches[0]; e.x = te.clientX; e.y = te.clientY; } if (!isMouseDown) return false const moveX = e.x - originX const moveY = e.y - originY if (moveX < 0 || moveX + 38 >= w) return false self.slider.style.left = moveX + 'px' var blockLeft = (w - 40 - 20) / (w - 40) * moveX self.block.style.left = blockLeft + 'px' addClass(self.sliderContainer, 'sliderContainer_active') self.sliderMask.style.width = moveX + 'px' trail.push(moveY) } document.addEventListener('mousemove', mousemove ); document.addEventListener('touchmove', mousemove ); /**/ var mouseup = function(e){ if (!isMouseDown) return false isMouseDown = false if (e.x == originX) return false removeClass(self.sliderContainer, 'sliderContainer_active') self.trail = trail const {spliced, TuringTest} = self.verify() if (spliced) { if (TuringTest) { addClass(self.sliderContainer, 'sliderContainer_success') self.success && self.success() } else { addClass(self.sliderContainer, 'sliderContainer_fail') self.text[removed] = '再试一次' self.reset() } } else { addClass(self.sliderContainer, 'sliderContainer_fail') self.fail && self.fail() setTimeout(() => { self.reset() }, 1000) } } document.addEventListener('mouseup', mouseup ); document.addEventListener('touchend', mouseup ); } 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))
以下是html的 滑块容器:
<div id="slideCodeBar" class="line slideCode"> <!--<span class="icon"><img src="/core/css/xficon/jt.png"/></span> <a class="button ">请按住滑块 拖至右边</a>--> <div id="slideCodeBox" class="slideCodeBox"> <div id="captcha" style="position: relative"></div></div> </div>
以下是 css 代码:
.sliderContainer { position: relative; text-align: left; height: 40px; line-height: 40px; background-color: #e6e6e6; color: #9999; border: 1px solid #abb6c6; border-radius: 3px; } .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 { } .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 { } .sliderContainer_active .sliderText, .sliderContainer_success .sliderText, .sliderContainer_fail .sliderText { display: none; } .sliderText{ margin-left: 50px; } .sliderMask { position: absolute; left: 1; top: 1; height: 38px; border: 0 solid #1991FA; background: #D1E9FE; width: 40px; } .slider { position: absolute; top: 1; left: 1; width: 42px; 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 { } .sliderIcon { position: absolute; top: 1px; left: 1px; width: 40px; height: 40px; background: url(./xficon/jt.png) center center no-repeat; } .refreshIcon { position: absolute; right: 0; top: 0; width: 34px; height: 34px; cursor: pointer; background: url(./xficon/icon_light.f13cff3.png) 0 -437px; background-size: 34px 471px; }
好了,这个就是简单的滑块验证效果,可以用于发送短信验证码前,要用户先 进行滑块验证,目的是防止机器人提交表单,避免造成随意发送短信的后果。
好了,本次的滑块验证的js效果 开发全部介绍完整了,感谢您的关注。
上一篇:没有了
实用工具: JSON字符串格式化 | js压缩代码格式化工具 | 异步XMLHttpRequests库axios.js文档 | vue-axios文档 | Go语言文档