
滑塊拼圖型的驗證方式已經流行起來,多數的實現方式是直接加載兩張分割好的圖片。現在用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);
}
