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);
    }

  

posted @ 2018-07-11 16:48  前端大兵  阅读(4375)  评论(1编辑  收藏  举报