上图为网易云盾的滑动拼图验证码,同一张图片裁剪的位置是固定的,猜测其应该有一个专门的图片库,保存了被裁剪的背景图和可移动的滑块。个人想法是,随机生成图片,随机生成位置,再用canvas裁剪出滑块和背景图。下面介绍具体步骤。html
首先随便找一张图片渲染到canvas上,这里#canvas做为画布,#block做为裁剪出来的小滑块。git
<canvas width="310" height="155" id="canvas"></canvas> <canvas width="310" height="155" id="block"></canvas>
var canvas = document.getElementById('canvas') var block = document.getElementById('block') var canvas_ctx = canvas.getContext('2d') var block_ctx = block.getContext('2d') var img = document.createElement('img') img.onload = function() { canvas_ctx.drawImage(img, 0, 0, 310, 155) block_ctx.drawImage(img, 0, 0, 310, 155) }; img.src = 'img.jpg'
下面考虑如何裁剪出拼图的形状,拼图形状比较复杂,首先咱们画一个正方形,接着上边的代码写:github
var x = 150, y = 40, w = 42, r = 10, PI = Math.PI function draw(ctx) { ctx.beginPath() ctx.moveTo(x, y) ctx.lineTo(x + w, y) ctx.lineTo(x + w, y + w) ctx.lineTo(x, y + w) ctx.clip() } draw(canvas_ctx) draw(block_ctx)
x,y为正方形左上角的坐标,如今先随便写后边生成的时候用随机数,w为正方形的边长,r为后边画缺口的圆的半径。咱们先把绘图过程用函数封装起来,方便后面同时操做背景和滑块。用clip()方法裁剪图片后生成一个正方形。
web
接下来画正方形上边和右边的圆形:canvas
function draw(ctx) { ctx.beginPath() ctx.moveTo(x,y) + ctx.lineTo(x+w/2,y) + ctx.arc(x+w/2,y-r+2, r,0,2*PI) // + ctx.lineTo(x+w/2,y) ctx.lineTo(x+w,y) + ctx.lineTo(x+w,y+w/2) + ctx.arc(x+w+r-2,y+w/2,r,0,2*PI) // + ctx.lineTo(x+w,y+w/2) ctx.lineTo(x+w,y+w) ctx.lineTo(x,y+w) ctx.lineTo(x,y) ctx.clip() }
两处注释的位置将圆心往内偏移了2px,实现缺口的样式。而后是左边空心的部分,因为clip是裁剪路径内的部分,所以直接像上面画圆是不行的,咱们开启一条新的路径,而后画圆将这个正方形“遮盖”出一个缺口,这里会用到globalCompositeOperation 属性,'xor'顾名思义。代码接上边:安全
function draw(ctx) { ctx.beginPath() ... ctx.lineTo(x,y) ctx.clip() + ctx.beginPath() + ctx.arc(x,y+w/2, r,1.5*PI,0.5*PI) // 只须要画正方形内的半圆就行,方便背景图片的裁剪 + ctx.globalCompositeOperation = "xor" + ctx.fill() }
如今一个基本的拼图形状有了,咱们调整#block的大小,并将裁剪出来的滑块放入#block中:函数
img.onload = function() { ctx.drawImage(img, 0, 0, 310, 155) block_ctx.drawImage(img, 0, 0, 310, 155) + var blockWidth = w + r * 2 + var _y = y - r * 2 + 2 // 滑块实际的y坐标 + var ImageData = block_ctx.getImageData(x, _y, blockWidth, blockWidth) + block.width = blockWidth + block_ctx.putImageData(ImageData, 0, _y) }
如今咱们须要把左边画布展现原来的图片,而且抠掉中间滑块的部分,这里画路径的过程都是同样的,惟一不一样只是clip()那里改为fill()便可实现效果,咱们前面已经把画路径的过程封装成函数了,稍做改动便可:web安全
- function draw(ctx) { + function draw(ctx, operation) { ... - ctx.clip() + ctx.fillStyle = '#fff' + ctx[operation]() ... } + draw(canvas_ctx, 'fill') + draw(block_ctx, 'clip')
接下来就是写样式了,略过:
code
而后就是写拖动事件,咱们能够在鼠标按下时记录鼠标位置,而后在拖动时给滑块设置left值。最后在松开鼠标时,判断滑块此时的left值和最开始裁剪滑块时的x值,若是在必定范围内就算验证经过,不然验证失败。htm
最后再加上随机图片和随机剪切位置,基本就ok了。另外能够判断下鼠标移动时y轴的变化,以判断是不是“人”在操做,固然web安全这块神魔乱舞的,我就不班门弄斧了,只是作个简单判断就行。
后边代码比较杂乱,就不贴上来了,查看完整代码点这里 演示地址点这里