滑塊拼圖型的驗證方式已經流行起來,多數的實現方式是直接加載兩張分割好的圖片。現在用canvas去自動修剪圖片,節省修圖工作量和http請求:
- 加載一張整圖,用canvas切割缺口,缺口位置在固定范圍內隨機
- 點擊刷新按鈕重新加載和切割
- 滑塊響應拖動,實時更新缺口位置
- 拖動結束時計算位置是否匹配,允許一定誤差
DOM結構如下:
1 <div class="verification"> 2 <div class="verPicture"> 3 <!-- 圖片未加載時顯示提示 --> 4 <div class="verLoading">正在加載 ...</div> 5 <!-- 刷新按鈕 --> 6 <div class="verRefresh" title="刷新"></div> 7 <!-- 存放圖片元素 --> 8 <canvas id="verCanvas" width="285" height="145"></canvas> 9 </div> 10 <div class="verSlider"> 11 <p>向右滑動滑塊填充拼圖</p> 12 <!-- 滑塊 --> 13 <div class="verSliderBlock"></div> 14 </div> 15 </div>
圖片和canvas大一統一,此例以285*145為准,缺口切成正方形:
var imgs = ["verify.png", "verify1.png", "verify2.png", "verify3.png", "verify4.png"] // 備用的驗證圖片 var imgSrc = imgs[parseInt(Math.random() * 5)]; //隨機一個圖片地址 var slider = $(".verSliderBlock")[0]; var canvas = $("#verCanvas")[0]; var context = canvas.getContext("2d"); var img = document.createElement('img'); var rightDistance; //記錄正確的移動距離 var topDistance; //記錄缺口離頂部的距離 var slideFlag = false; //標記滑塊是否處於移動狀態 var origin = 0; //標記移動起始位置的x坐標 // distance標記移動的距離,值改變時觸發滑塊移動和canvas重繪 Object.defineProperty(window, "distance", { set: function(distance) { this.value = distance; if (distance > 0 && distance < 246) { $(slider).css("left", distance); draw(distance); } else if (distance <= 0) { $(slider).css("left", 0); draw(0); } else { $(slider).css("left", 247); draw(247); } }, get: function() { return this.value; } }); initCanvas(imgSrc); // 刷新事件 function verRefresh() { unbindSlide(); imgSrc = imgs[parseInt(Math.random() * 5)]; slideFlag = false; distance = 0; origin = 0; $(".verSlider p").html("向右滑動滑塊填充拼圖").removeClass(); initCanvas(imgSrc); } $(".verRefresh").click(verRefresh); // 判斷位置是否正確,用於滑動結束時 function judgeDistance() { if (distance > rightDistance - 3 && distance < rightDistance + 3) { $(slider).css("left", 247); $(".verSlider p").html("驗證成功").removeClass("hide").addClass("success"); setTimeout(function() { console.log("success") }, 1000) } else { distance = 0; $(".verSlider p").html("驗證失敗").removeClass("hide").addClass("fail"); setTimeout(verRefresh, 1000) } } /**** 滑動監聽 ****/ function onmousedown(e) { slideFlag = true; $(slider).addClass("active"); origin = e.x; } function onmousemove(e) { if (slideFlag) { // 隱藏滑動提示文字 $(".verSlider p").addClass("hide"); //計算位置 distance = e.x - origin; } } function onmouseup(e) { if (slideFlag) { slideFlag = false; distance = e.x - origin; $(slider).removeClass("active"); judgeDistance() } } function bindSlide() { slider.addEventListener("mousedown", onmousedown); document.addEventListener("mousemove", onmousemove, true); document.addEventListener("mouseup", onmouseup, true); } function unbindSlide() { slider.removeEventListener("mousedown", onmousedown); document.removeEventListener("mousemove", onmousemove, true); document.removeEventListener("mouseup", onmouseup, true); } /**** canvas對象 ****/ function initCanvas(img_src) { $(".verLoading").show(); img.src = img_src; img.onload = function() { $(".verLoading").hide(); bindSlide(slider); // 獲取隨機位置 rightDistance = parseInt(Math.random() * 100 + 145); topDistance = parseInt(Math.random() * 80 + 10); draw(); }; } function draw(left) { context.clearRect(0, 0, canvas.width, canvas.height) // 繪制整圖和半透明缺口 context.drawImage(img, 0, 0, canvas.width, canvas.height); context.globalAlpha = 0.9; context.fillStyle = "#fff"; context.fillRect(rightDistance, topDistance, 35, 35); context.globalAlpha = 0.5; context.strokeStyle = "#000"; context.lineWidth = 1; context.strokeRect(rightDistance, topDistance, 35, 35); // 繪制脫離的缺口 context.globalAlpha = 1; context.shadowBlur = 10; context.shadowColor = "#fff"; context.strokeStyle = "#fff"; context.strokeRect(left || 2, topDistance, 35, 35); context.drawImage(img, rightDistance, topDistance, 35, 35, left || 2, topDistance, 35, 35); }