canvas验证码 - 滑块拼图
滑块拼图型的验证方式已经流行起来,多数的实现方式是直接加载两张分割好的图片。现在用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); }