上圖爲網易雲盾的滑動拼圖驗證碼,同一張圖片裁剪的位置是固定的,猜測其應該有一個專門的圖片庫,保存了被裁剪的背景圖和可移動的滑塊。個人想法是,隨機生成圖片,隨機生成位置,再用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安全這塊神魔亂舞的,我就不班門弄斧了,只是作個簡單判斷就行。
後邊代碼比較雜亂,就不貼上來了,查看完整代碼點這裏 演示地址點這裏