Vue实现滑动拼图验证码功能

缘由:之前看哔哩哔哩官网登录的时候有一个拼图验证码,很好奇怎么去实现。然后就想着自己弄一个。先给大家看我的最终效果。后面再一点点拆解代码。

为什么想着写这个功能呢,主要在于拼图验证码在前端这里会比较复杂并且深入。相比文字拼写,12306的图片验证码都没有拼图验证码对前端的要求来的复杂,和难。

我总结下知识点:

1、弹窗功能

2、弹窗基于元素定位

3、元素拖动

4、canvas绘图

5、基础逻辑

一、弹窗和弹窗组件

抱歉,这里我偷懒了直接用了elementUI的el-popover组件,所以小伙伴不懂的直接看elementUI官网的说明。

我个人也研究和编写了这块的组件功能(基于popper.js)

二、编写基础结构

这块属于html的基础内容,也就标题党了

三、canvas绘制图片

1、canvas绘制外部img图片

代码:

let mainDom = document.querySelector("#codeImg");
let bg = mainDom.getContext("2d");
let width = mainDom.width;
let height = mainDom.height;

let blockDom = document.querySelector("#sliderBlock");
let block = blockDom.getContext("2d");
//重新赋值,让canvas进行重新绘制
blockDom.height = height;
mainDom.height = height;

let imgsrc = require("../assets/images/back.jpg");
let img = document.createElement("img");
img.style.objectFit = "scale-down";
img.src = imgsrc;
img.onload = function() {
 bg.drawImage(img, 0, 0, width, height);
 block.drawImage(img, 0, 0, width, height);
};

这里我绘制了两个canvas,因为一个是背景一个是滑块

核心在于

let mainDom = document.querySelector("#codeImg");
let imgsrc = require("../assets/images/back.jpg");
let bg = mainDom.getContext("2d");
let img = document.createElement("img");
img.onload = function() {
  bg.drawImage(img, 0, 0, width, height);
};

2、canvas绘制滑块部分

就是这个图,这个有一些知识点,不难,但是很复杂。

具体请看:https://www.w3school.com.cn/tags/html_ref_canvas.asp

代码部分:

drawBlock(ctx, xy = { x: 254, y: 109, r: 9 }, type) {
 let x = xy.x,
  y = xy.y,
  r = xy.r,
  w = 40;
 let PI = Math.PI;
 //绘制
 ctx.beginPath();
 //left
 ctx.moveTo(x, y);
 //top
 ctx.arc(x + (w + 5) / 2, y, r, -PI, 0, true);
 ctx.lineTo(x + w + 5, y);
 //right
 ctx.arc(x + w + 5, y + w / 2, r, 1.5 * PI, 0.5 * PI, false);
 ctx.lineTo(x + w + 5, y + w);
 //bottom
 ctx.arc(x + (w + 5) / 2, y + w, r, 0, PI, false);
 ctx.lineTo(x, y + w);
 ctx.arc(x, y + w / 2, r, 0.5 * PI, 1.5 * PI, true);
 ctx.lineTo(x, y);
 //修饰,没有会看不出效果
 ctx.lineWidth = 1;
 ctx.fillStyle = "rgba(255, 255, 255, 0.5)";
 ctx.strokeStyle = "rgba(255, 255, 255, 0.5)";
 ctx.stroke();
 ctx[type]();
 ctx.globalCompositeOperation = "xor";
}

解释下:

参数是传入canvas对象

x,y轴数据

剪切还是填充的canvas函数(fill,clip)

绘制难点:(很重要,不然你没法理解它怎么绘制的)

绘制主要是需要理解这里的绘制是根据你设置一个起始点坐标,然后你绘制第二次的时候线就会连接到第二个点,依次连接最后回到原点就形成一个完整的图形。

圆形的绘制:来自:https://www.w3school.com.cn/tags/canvas_arc.asp

用的是arc参数,主要是看这个图

fill是用于填充绘制的部分,clip是裁剪出绘制的部分,利用这个就可以出现一个扣掉的图片和一个裁剪出来的图片。

完成之后就是我的那个函数了。大家可以直接拿去用。

3、让元素跟随鼠标点击之后滑动

这里其实原理非常简单,就是有一个注意点。

原理:

鼠标点击之后记录当前坐标,然后随着( mousemove )滚动的时候修改元素的left和top值就行了。

还有一点就是鼠标快速滑动会导致丢失滑动效果,这里需要用document,不能是元素级别的监听。

元素上面我只需要鉴定按下 mousedown

代码:

//鼠标按下
drag(e) {
 console.log("鼠标按下", e);
 let dom = e.target; //dom元素
 let slider = document.querySelector("#sliderBlock"); //滑块dom
 const downCoordinate = { x: e.x, y: e.y };

 //正确的滑块数据
 let checkx = Number(this.slider.mx) - Number(this.slider.bx);
 //x轴数据
 let x = 0;
 const move = moveEV => {
  x = moveEV.x - downCoordinate.x;
  //y = moveEV.y - downCoordinate.y;
  if (x >= 251 || x <= 0) return false;
  dom.style.left = x + "px";
  //dom.style.top = y + "px";
  slider.style.left = x + "px";
 };

 const up = () => {
  document.removeEventListener("mousemove", move);
  document.removeEventListener("mouseup", up);
  dom.style.left = "";

  console.log(x, checkx);
  let max = checkx - 5;
  let min = checkx - 10;
  //允许正负误差1
  if ((max >= x && x >= min) || x === checkx) {
   console.log("滑动解锁成功");
   this.puzzle = true;
   this.tips = "验证成功";
   setTimeout(() => {
    this.visible = false;
   }, 500);
  } else {
   console.log("拼图位置不正确");
   this.tips = "验证失败,请重试";
   this.puzzle = false;
   this.canvasInit();
  }
 };

 document.addEventListener("mousemove", move);
 document.addEventListener("mouseup", up);
}

4、总结

核心点比较多,写过之后发现不难,关键在于去写

个人该页面git地址:https://github.com/ht-sauce/dream

该页面处于项目的

路由访问为:http://localhost:8080/consumer

5、完整的页面代码

<template> <div id="login">
  <el-form class="loginFrom" :model="logindata" :rules="rules" ref="ruleForm">
   <el-form-item class="login-item">
    <h1 class="login-title">海天酱油登录中心</h1>
   </el-form-item>
   <el-form-item prop="userName">
    <el-input
     class="login-inputorbuttom"
     prefix-icon="el-icon-user"
     placeholder="登录名"
     v-model="logindata.userName"
    ></el-input>
   </el-form-item>
   <el-form-item prop="password">
    <el-input
     class="login-inputorbuttom"
     prefix-icon="el-icon-lock"
     placeholder="密码"
     v-model="logindata.password"
    ></el-input>
   </el-form-item>
   <!--<el-form-item prop="verificationCode">
    <el-input
     class="login-inputorbuttom"
     v-model="logindata.verificationCode"
    ></el-input>
   </el-form-item>-->
   <el-form-item class="login-item">
    <el-button
     class="login-inputorbuttom login-bottom"
     type="primary"
     v-popover:popover
     @click="visible = !visible"
     >登 录</el-button
    >
   </el-form-item>
  </el-form>
  <!--验证码弹窗-->
  <el-popover
   popper-class="slidingPictures"
   ref="popover"
   trigger="manual"
   v-model="visible"
  >
   <div class="sliding-pictures">
    <div class="vimg">
     <canvas id="sliderBlock"></canvas>
     <canvas id="codeImg"></canvas>
    </div>
    <div class="slider">
     <div class="track" :class="{ pintuTrue: puzzle }">
      {{ tips }}
     </div>
     <div class="button el-icon-s-grid" @mousedown.prevent="drag"></div>
    </div>
    <div class="operation">
     <span
      title="关闭验证码"
      @click="visible = false"
      class="el-icon-circle-close"
     ></span>
     <span
      title="刷新验证码"
      @click="canvasInit"
      class="el-icon-refresh-left"
     ></span>
    </div>
   </div>
  </el-popover>
 </div>
</template>
<script>
export default {
 name: "login",
 data() {
  return {
   tips: "拖动左边滑块完成上方拼图",
   logindata: {
    userName: "",
    password: "",
    verificationCode: ""
   },
   rules: {},
   visible: false,
   //滑块x轴数据
   slider: {
    mx: 0,
    bx: 0
   },
   //拼图是否正确
   puzzle: false
  };
 },
 watch: {
  visible(e) {
   if (e === true) {
    this.canvasInit();
    this.puzzle = false;
   }
  }
 },
 beforeCreate() {},
 created() {},
 beforeMount() {},
 mounted() {},
 methods: {
  //拼图验证码初始化
  canvasInit() {
   //生成指定区间的随机数
   const random = (min, max) => {
    return Math.floor(Math.random() * (max - min + 1) + min);
   };
   //x: 254, y: 109
   let mx = random(127, 244),
    bx = random(10, 128),
    y = random(10, 99);
   this.slider = { mx, bx };
   this.draw(mx, bx, y);
  },
  //鼠标按下
  drag(e) {
   console.log("鼠标按下", e);
   let dom = e.target; //dom元素
   let slider = document.querySelector("#sliderBlock"); //滑块dom
   const downCoordinate = { x: e.x, y: e.y };
   //正确的滑块数据
   let checkx = Number(this.slider.mx) - Number(this.slider.bx);
   //x轴数据
   let x = 0;
   const move = moveEV => {
    x = moveEV.x - downCoordinate.x;
    //y = moveEV.y - downCoordinate.y;
    if (x >= 251 || x <= 0) return false;
    dom.style.left = x + "px";
    //dom.style.top = y + "px";
    slider.style.left = x + "px";
   };
   const up = () => {
    document.removeEventListener("mousemove", move);
    document.removeEventListener("mouseup", up);
    dom.style.left = "";
    console.log(x, checkx);
    let max = checkx - 5;
    let min = checkx - 10;
    //允许正负误差1
    if ((max >= x && x >= min) || x === checkx) {
     console.log("滑动解锁成功");
     this.puzzle = true;
     this.tips = "验证成功";
     setTimeout(() => {
      this.visible = false;
     }, 500);
    } else {
     console.log("拼图位置不正确");
     this.tips = "验证失败,请重试";
     this.puzzle = false;
     this.canvasInit();
    }
   };
   document.addEventListener("mousemove", move);
   document.addEventListener("mouseup", up);
  },
  draw(mx = 200, bx = 20, y = 50) {
   let mainDom = document.querySelector("#codeImg");
   let bg = mainDom.getContext("2d");
   let width = mainDom.width;
   let height = mainDom.height;
   let blockDom = document.querySelector("#sliderBlock");
   let block = blockDom.getContext("2d");
   //重新赋值,让canvas进行重新绘制
   blockDom.height = height;
   mainDom.height = height;
   let imgsrc = require("../assets/images/back.jpg");
   let img = document.createElement("img");
   img.style.objectFit = "scale-down";
   img.src = imgsrc;
   img.onload = function() {
    bg.drawImage(img, 0, 0, width, height);
    block.drawImage(img, 0, 0, width, height);
   };
   let mainxy = { x: mx, y: y, r: 9 };
   let blockxy = { x: bx, y: y, r: 9 };
   this.drawBlock(bg, mainxy, "fill");
   this.drawBlock(block, blockxy, "clip");
  },
  //绘制拼图
  drawBlock(ctx, xy = { x: 254, y: 109, r: 9 }, type) {
   let x = xy.x,
    y = xy.y,
    r = xy.r,
    w = 40;
   let PI = Math.PI;
   //绘制
   ctx.beginPath();
   //left
   ctx.moveTo(x, y);
   //top
   ctx.arc(x + (w + 5) / 2, y, r, -PI, 0, true);
   ctx.lineTo(x + w + 5, y);
   //right
   ctx.arc(x + w + 5, y + w / 2, r, 1.5 * PI, 0.5 * PI, false);
   ctx.lineTo(x + w + 5, y + w);
   //bottom
   ctx.arc(x + (w + 5) / 2, y + w, r, 0, PI, false);
   ctx.lineTo(x, y + w);
   ctx.arc(x, y + w / 2, r, 0.5 * PI, 1.5 * PI, true);
   ctx.lineTo(x, y);
   //修饰,没有会看不出效果
   ctx.lineWidth = 1;
   ctx.fillStyle = "rgba(255, 255, 255, 0.5)";
   ctx.strokeStyle = "rgba(255, 255, 255, 0.5)";
   ctx.stroke();
   ctx[type]();
   ctx.globalCompositeOperation = "xor";
  }
 }
};
</script>
<style>
.slidingPictures {
 padding: 0;
 width: 300px;
 border-radius: 2px;
}
</style>
<style scoped lang="scss">
#login {
 display: flex;
 flex-flow: row;
 justify-content: flex-end;
 align-items: center;
 width: 100%;
 height: 100%;
 background-image: url("../assets/images/back.jpg");
 background-size: 100% 100%;
 .loginFrom {
  width: 300px;
  margin-top: -10vw;
  margin-right: 10vw;
  .login-item {
   display: flex;
   justify-content: center;
   align-items: center;
  }
  .login-title {
   color: #ffffff;
   font-size: 16px;
   margin-bottom: 10px;
  }
  .login-bottom {
   margin-top: 15px;
  }
  .login-bottom:hover {
   background: rgba(28, 136, 188, 0.5);
  }
  .login-bottom:active {
   background: rgba(228, 199, 200, 0.5);
  }
  /deep/.login-inputorbuttom {
   height: 40px;
   width: 300px;
   background: rgba(57, 108, 158, 0.5);
   border-radius: 20px;
   border: #396c9e 1px solid;
   font-size: 14px;
   color: #ffffff;
   .el-input--small,
   .el-input__inner {
    line-height: 43px;
    border: none;
    color: #ffffff;
    font-size: 14px;
    height: 40px;
    border-radius: 20px;
    background: transparent;
    text-align: center;
   }
   .el-input__icon {
    line-height: 40px;
    font-size: 16px;
   }
  }
 }
}
/*该样式最终是以弹窗插入*/
.sliding-pictures {
 width: 100%;
 .vimg {
  width: 100%;
  height: 170px;
  #codeImg,
  #sliderBlock {
   padding: 7px 7px 0 7px;
   width: inherit;
   height: inherit;
  }
  #codeImg {
   //display: none;
  }
  #sliderBlock {
   position: absolute;
   z-index: 4000;
  }
 }
 .slider {
  width: 100%;
  height: 65px;
  border-bottom: #c7c9d0 1px solid;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  .track {
   margin-left: 7px;
   width: 286px;
   height: 38px;
   background: rgba(28, 136, 188, 0.5);
   border-radius: 25px;
   font-size: 14px;
   line-height: 38px;
   padding-right: 15px;
   padding-left: 70px;
  }
  .pintuTrue {
   background: #67c23a;
   color: #ffffff;
  }
  .button {
   position: absolute;
   width: 50px;
   height: 50px;
   line-height: 48px;
   background: #ffffff;
   box-shadow: #b9bdc8 0 0 3px;
   border-radius: 50%;
   left: 7px;
   text-align: center;
   font-size: 28px;
   color: #3e5d8b;
   &:hover {
    color: #2181bd;
   }
  }
 }
 .operation {
  width: 100%;
  height: 40px;
  > span {
   color: #9fa3ac;
   display: inline-block;
   width: 40px;
   font-size: 25px;
   line-height: 40px;
   text-align: center;
   &:hover {
    background: #e2e8f5;
   }
  }
 }
}
</style>

总结

以上所述是小编给大家介绍的Vue实现滑动拼图验证码功能,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小会及时回复大家的!

(0)

相关推荐

  • Vue实现滑动拼图验证码功能

    缘由:之前看哔哩哔哩官网登录的时候有一个拼图验证码,很好奇怎么去实现.然后就想着自己弄一个.先给大家看我的最终效果.后面再一点点拆解代码. 为什么想着写这个功能呢,主要在于拼图验证码在前端这里会比较复杂并且深入.相比文字拼写,12306的图片验证码都没有拼图验证码对前端的要求来的复杂,和难. 我总结下知识点: 1.弹窗功能 2.弹窗基于元素定位 3.元素拖动 4.canvas绘图 5.基础逻辑 一.弹窗和弹窗组件 抱歉,这里我偷懒了直接用了elementUI的el-popover组件,所以小伙伴

  • js+canvas实现滑动拼图验证码功能

    上图为网易云盾的滑动拼图验证码,其应该有一个专门的图片库,裁剪的位置是固定的.我的想法是,随机生成图片,随机生成位置,再用canvas裁剪出滑块和背景图.下面介绍具体步骤. 首先随便找一张图片渲染到canvas上,这里#canvas作为画布,#block作为裁剪出来的小滑块. <canvas width="310" height="155" id="canvas"></canvas> <canvas width=&q

  • Android实现滑块拼图验证码功能

    滑块拼图验证码应该算是很常见的功能了,验证码是可以区分用户是人还是机器.可以防止破解密码.刷票等恶意行为.本文将介绍Android拼图滑块验证码控件的实现过程.希望能帮助到大家. 先看最终的效果图: 本文只是做了个Demo,并没有加入到实际的项目中,所以各位童鞋可以根据自己的需求就行修改即可. 一.实现步骤: 1.定义自定义属性: 2.确认目标位置,这里使用的是阴影图片来遮盖背景图片: 3.创建与目标位置相结合的滑块图片: 4.设置目标阴影图片和滑块图片可以随机旋转,并保持一致: 5.创建拖拽条

  • Android 简单的实现滑块拼图验证码功能

    实现滑块拼图验证码功能之前已经写过一篇了,上一篇使用的是自定义控件的方式实现这个功能,主要还是想让童鞋们知其然更知其所以然,还没看的童鞋可以先看看Android实现滑块拼图验证码功能这篇. 在项目的开发过程中,时间比较紧急,通过自定义的方式很显然需要耗费很多时间去写,所以我们需要使用更简单的方式实现,这样会帮我们节省很多时间去解决其它的问题,使用依赖库的方式显然是最节省时间的,下面我们来看看是怎么实现的吧! 本篇主要从两方面进行介绍: 1.使用依赖库实现最终的功能: 2.依赖库的介绍: 实现过程

  • JS实现PC手机端和嵌入式滑动拼图验证码三种效果

    PC和手机端网站滑动拼图验证码效果源码,同时包涵了弹出式Demo,使用ajax形式提交二次验证码所需的验证结果值,嵌入式Demo,使用表单形式提交二次验证所需的验证结果值,移动端手动实现弹出式Demo三种效果 首先要确认前端使用页面,比如登陆页面 <script src="http://code.jquery.com/jquery-1.12.3.min.js"></script> <script src="http://static.geetes

  • VUE接入腾讯验证码功能(滑块验证)备忘

    最近在用VUE做个简单的用户系统,登录注册需要验证码,想找个那种拖动的,找geetest居然已经不面向小客户了(或者说只有收费套餐). 腾讯防水墙的验证码免费使用,有2000/小时的免费额度,对于小网站完全足够了,阿里应该也有,我看discuz有插件直接能用,但没找到入口 腾讯的在这,和腾讯云无关:https://007.qq.com/captcha/#/  申请api很简单,QQ登录,创建应用,ID和secretkey就出来了,直接在文档里展示,赞一个. vue有人做了封装了geetest+腾

  • Vue 实现登录界面验证码功能

    登录界面 SIdentify 创建验证码组件,实现绘画出图片验证码 <template> <div class="s-canvas"> <canvas id="s-canvas" :width="contentWidth" :height="contentHeight"></canvas> </div> </template> <script>

  • JS实现滑动拼图验证功能完整示例

    本文实例讲述了JS实现滑动拼图验证功能.分享给大家供大家参考,具体如下: 先看一下效果图: 设置画布滑块属性 const l = 42, // 滑块边长 r = 10, // 滑块半径 w = 310, // canvas宽度 h = 155, // canvas高度 PI = Math.PI const L = l + r * 2 // 滑块实际边长 设置背景图片: 图片链接地址可以自行更换 function getRandomImg() { return 'https://picsum.ph

  • Flutter实现滑动块验证码功能

    Flutter实现滑动块验证码功能,供大家参考,具体内容如下 本文实现的是一个用于登录时,向右滑动滑动块到最右边完成验证的一个功能.当滑动未到最右边时,滑动块回弹回左边起始位置. import 'package:flutter/material.dart';   class SlideVerifyWidget extends StatefulWidget{     /// 背景色   final Color backgroundColor;   /// 滑动过的颜色   final Color

  • Android滑动拼图验证码控件使用方法详解

    简介: 很多软件为了安全防止恶意攻击,会在登录/注册时进行人机验证,常见的人机验证方式有:谷歌点击复选框进行验证,输入验证码验证,短信验证码,语音验证,文字按顺序选择在图片上点击,滑动拼图验证等. 效果图: 代码实现: 1.滑块视图类:SlideImageView.java.实现随机选取拼图位置,对拼图位置进行验证等功能. public class SlideImageView extends View { Bitmap bitmap; Bitmap drawBitmap; Bitmap ver

随机推荐