Canvas - 验证码设计

Canvas - 验证码设计

🐯使用div去写验证方式

朋友让我写一个纯前端的验证方法,唯唯诺诺的写了一个简单的加减乘除运算,总感觉少了点什么

1.gif

我看了一下可能是少了一个背景,于是我加上了背景

2.gif

贴出此效果--代码

style的内容

 <style>
    .verificationCode{
      width: 100px;
      height: 50px;
      display: flex;
      border: 1px black solid;
      align-items: center;
      justify-content: center;
      user-select: none;
      cursor: pointer;
    }
    .firstNumber{
      font-size: 26px;
      margin-right: 5px;
    }
    .operationNumber{
      font-size: 26px;
      margin-right: 5px;
    }
    .secodeNumber{
      font-size: 26px;
    }
    .inputNumber{
      padding: 9px;
      width: 70px;
      margin-left: 15px;
      font-size: 24px;
    }
    .inputNumber::-webkit-inner-spin-button{
      appearance: none;
    }
    .baibai{
      display: flex;
      margin: 0 auto;
    }
  </style>
复制代码

body的内容

<body>
  <div class="baibai">
    <div class="verificationCode">
        <div class="firstNumber"></div>
        <div class="operationNumber"></div>
        <div class="secodeNumber"></div>
    </div>
     <label style="line-height: 50px;height: 50px;margin: 0 15px;"> 正确答案</label>
    <input class="inputNumber" type="number">
  </div>

    <script>
       let firstNumberNode = document.querySelector('.firstNumber');
       let operationNumberNode = document.querySelector('.operationNumber');
       let secodeNumberNode = document.querySelector('.secodeNumber');
       let verificationCodeNode = document.querySelector('.verificationCode');
       let inputNode = document.querySelector('.inputNumber');
       randomOption(firstNumberNode,operationNumberNode,secodeNumberNode,inputNode);
       verificationCodeNode.style.backgroundColor = randomto16Sting();
       verificationCodeNode.addEventListener('click',()=>{
           randomOption(firstNumberNode,operationNumberNode,secodeNumberNode,inputNode);  
           verificationCodeNode.style.backgroundColor = randomto16Sting();
       })

        //变成16位的
      function randomto16Sting (){
        return '#'+parseInt(randomNumber(0xFFFFFF)).toString(16);
      }
      //随机数
      function randomNumber(number){
        return Math.random()*number
      }

      function randomOption(firstNumberNode,operationNumberNode,secodeNumberNode,inputNode){
       let firstNumber = Math.ceil(Math.random(1)*10);
       let operationNumber = Math.ceil(Math.random()*4);
       let secodeNumber = Math.ceil(Math.random(1)*10);
       let answer = 0;
       let mul = 0; //交互使用
        switch (operationNumber) {
          case 1:
            operation="+";
            answer = firstNumber+secodeNumber;
            break;
            case 2:
            operation="-"
            if(firstNumber < secodeNumber){
              mul = firstNumber ;
              firstNumber = secodeNumber;
              secodeNumber = mul;
            }
            answer = firstNumber - secodeNumber;
            break;
            case 3:
            operation="×"
            answer = firstNumber*secodeNumber;
            break;
            case 4:
            operation="÷"
           let answerFirst = firstNumber*secodeNumber;
           mul = firstNumber ;
           firstNumber = answerFirst;
           answer  = mul;
            break;
          default:
            break;
        }
        
        let dataArray = [firstNumber,operation,secodeNumber,answer];
           firstNumberNode.innerHTML = dataArray[0];
           operationNumberNode.innerHTML = dataArray[1];
           secodeNumberNode.innerHTML = dataArray[2];
          inputNode.value =  dataArray[3];
          firstNumberNode.style.color = randomto16Sting();
          operationNumberNode.style.color = randomto16Sting();
          secodeNumberNode.style.color = randomto16Sting();
      }
    </script>
</body>
复制代码

说明一下

大致流程:

  1. 首先获取结点
  2. 利用randomOption:这个函数生成随机数,和生成我们小学学过的加减乘除的符号,同时也是为了生成这个算法的答案。该函数中的除法,我采用的是,等到两个数之后,利用乘法获取答案,该答案就是被除者,使用数据交换,在输出数据。(这个效果是为了排除,一味的随机,除不尽问题)。
  3. 点击在调用一次randomOption函数,就会刷新。

缺陷

突然发现,单一的界面没有了干扰图(就那些花花绿绿的圆球与线条),我大胆的想使用div去画,当然这样的想法确实是🐯了。

于是我打算采用了canvas去画画。、

🐯使用canvas画验证方式

华丽(花里)胡哨,里面画了球与线段,还让文字发生了旋转

3.gif

采取的canvas技术使用

  • 画圆
  • 画线段
  • 画文字

先说canvas的使用

页面中需要有canvas的节点

<!-- 对canvas画布的宽高进行设定-->
 <canvas id="canvas" width="100" height="50"></canvas>
复制代码

js中去获取与使用canvas

  let canvas = document.querySelector('#canvas'); //获取画布
  let ctx =canvas.getContext('2d'); //开启的2d的画布
复制代码

画圆

以下是设计代码

       //随机数
      function randomNumber(number){
        return Math.random()*number
      }
      //randomRange  画圆的半径大小取值
      function drawArc(randomRange){
        let x = randomNumber(canvas.width);
        let y = randomNumber(canvas.height);
        let r = randomNumber(randomRange)+2;
        let to16Sting = randomto16Sting();
        ctx.beginPath();
        ctx.arc(x,y,r,0,Math.PI*2);
        ctx.fillStyle = to16Sting;
        ctx.fill();
        ctx.strokeStyle= to16Sting; //为了确保自身的颜色,不被下面的strokeStyle影响
        ctx.lineWidth = 1; //为了确保自身的长度,不被下面的lineWidth影响
        ctx.stroke();
        ctx.closePath();
      }
复制代码

效果图(为看见效果canvas扩大了10倍)

4.gif

画线段

以下是设计代码

   function drawLine(randomRange){
         ctx.beginPath();
          let x1 = randomNumber(canvas.width - 10)+10;
          let y1 = randomNumber(canvas.height - 10)+10;
          let x2 = randomNumber(canvas.width - 10)+10;
          let y2 = randomNumber(canvas.height - 10)+10;
          ctx.moveTo(x1,y1);
          ctx.lineTo(x2,y2);
          ctx.strokeStyle= randomto16Sting();
          ctx.lineWidth=randomNumber(randomRange)+1;
          ctx.stroke();
          ctx.closePath();
      }
复制代码

效果图(为看见效果canvas扩大了10倍)

5.gif

画文字

以下是设计代码

文字是带有旋转的

   //变成16位的
      function randomto16Sting (){
        return '#'+parseInt(randomNumber(0xFFFFFF)).toString(16);
      }
    //画字体
     function drawText(dataArray){
          //  ctx.translate(0,0);
          ctx.font = "26px Gaoel";
          ctx.textAlign ='center';
          ctx.textBaseline ='middle';

          ctx.fillText(dataArray[0],20,25);
          ctx.fillStyle = randomto16Sting();

          ctx.fillText(dataArray[1],50,25);
          ctx.fillStyle = randomto16Sting();

          ctx.fillText(dataArray[2],80,25);
          ctx.fillStyle = randomto16Sting();
     }
复制代码

效果图(为看见效果canvas扩大了10倍)

6.gif

朋友又又给我提问了,为什么字体不会旋转那,我看别人的验证码,都是歪歪扭扭的。

朋友既然提了,那就要搞啊!,自身的面子要装足

旋转角度设计代码

  function randomRotate(){
    let arr = [];
    let arr1 =[];
    let count = 0;
    //担心随机出的角度太差距,所以写出的一点调整
      while(count < 3 ){
        switch (count) {
          case 0:
          arr.push(randomNumber(10)) 
            break;
            case 1:
          arr.push(randomNumber(10))     
            break;
            case 2:
          arr.push(randomNumber(10)) 
          let sum = arr.reduce((a,b) =>{
              return a+b;
          },0)
          if(Math.max(...arr)/sum > 0.6){
              arr[arr.indexOf(Math.max(...arr))] = - sum/(sum*0.6);
          }else{
            arr[arr.indexOf(Math.max(...arr))] = - Math.max(...arr);
          }

          arr1 =  arr.map(element=>{
              return element*Math.PI/180;
          })

            break;
          default:
            break;
        }
        count ++ ;
      }
      //这个是为了存储上一次的数据,为了消除translate的下次旋转以当前位置角度开始。
      preRotateDeg.unshift(arr1);
      return arr1
  }
复制代码

字体使用代码

 function drawText(dataArray){
      //  ctx.translate(0,0);
      ctx.font = "26px Gaoel";
      ctx.textAlign ='center';
      ctx.textBaseline ='middle';
      let rotateNumber = randomRotate()
      // 以画布(0,0)作为了旋转点了,需要修正,也就是pre后退一步
      ctx.rotate(-preRotateDeg[1][0])
      ctx.rotate(rotateNumber[0]);
      ctx.fillText(dataArray[0],20,25);
      ctx.fillStyle = randomto16Sting();

      ctx.rotate(-preRotateDeg[1][1])
      ctx.rotate(rotateNumber[1]);
      ctx.fillText(dataArray[1],50,25);
      ctx.fillStyle = randomto16Sting();

      ctx.rotate(-preRotateDeg[1][2])
      ctx.rotate(rotateNumber[2]);
      ctx.fillText(dataArray[2],80,25);
      ctx.fillStyle = randomto16Sting();


 }
复制代码

效果图(为看见效果canvas扩大了10倍)

7.gif

🐯总体代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .verificationCode{
      width: 100px;
      height: 50px;
      display: flex;
      align-items: center;
      justify-content: center;
      user-select: none;
      cursor: pointer;
    }
    .inputNumber{
      padding: 9px;
      width: 70px;
      margin-left: 15px;
      font-size: 24px;
    }
    .inputNumber::-webkit-inner-spin-button{
      appearance: none;
    }
    .baibai{
      display: flex;
      margin: 0 auto;
    }
  </style>
</head>
<body>
  <div class="baibai">
    <div class="verificationCode">
      <canvas id="canvas" width="100" height="50"></canvas>
    </div>
     <label style="line-height: 50px;height: 50px;margin: 0 15px;"> 正确答案</label>
    <input class="inputNumber" type="number">
  </div>

    <script>
       let canvas = document.querySelector('#canvas');
       let ctx =canvas.getContext('2d');

       let verificationCodeNode = document.querySelector('.verificationCode');
       let inputNode = document.querySelector('.inputNumber');
       let preRotateDeg =[[0,0,0]];
       randomOption(inputNode);
       verificationCodeNode.addEventListener('click',()=>{
           randomOption(inputNode);  
           if(preRotateDeg.length == 3){
            preRotateDeg.pop()
           } 
       })
       // //定时30秒自动刷新一次 
      //  setInterval(()=>{
      //   randomOption(inputNode);
      //  } ,30000)
        //变成16位的颜色专用
      function randomto16Sting (){
        return '#'+parseInt(randomNumber(0xFFFFFF)).toString(16);
      }
      //随机数
      function randomNumber(number){
        return Math.random()*number
      }
      function randomRotate(){
        let arr = [];
        let arr1 =[];
        let count = 0;
        //担心随机出的角度太差距,所以写出的一点调整
          while(count < 3 ){
            switch (count) {
              case 0:
              arr.push(randomNumber(10)) 
                break;
                case 1:
              arr.push(randomNumber(10))     
                break;
                case 2:
              arr.push(randomNumber(10)) 
              let sum = arr.reduce((a,b) =>{
                  return a+b;
              },0)
              if(Math.max(...arr)/sum > 0.6){
                  arr[arr.indexOf(Math.max(...arr))] = - sum/(sum*0.6);
              }else{
                arr[arr.indexOf(Math.max(...arr))] = - Math.max(...arr);
              }
             
              arr1 =  arr.map(element=>{
                  return element*Math.PI/180;
              })

                break;
              default:
                break;
            }
            count ++ ;
          }
            //这个是为了存储上一次的数据,为了消除translate的下次旋转以当前位置角度开始。
          preRotateDeg.unshift(arr1);
          return arr1
      }
      function drawArc(randomRange){
        let x = randomNumber(canvas.width);
        let y = randomNumber(canvas.height);
        let r = randomNumber(randomRange)+2;
        let to16Sting = randomto16Sting();
        ctx.beginPath();
        ctx.arc(x,y,r,0,Math.PI*2);
        ctx.fillStyle = to16Sting;
        ctx.fill();
        ctx.strokeStyle= to16Sting;
        ctx.lineWidth = 1;
        ctx.stroke();
        ctx.closePath();
      }
      function drawLine(randomRange){
         ctx.beginPath();
          let x1 = randomNumber(canvas.width - 10)+10;
          let y1 = randomNumber(canvas.height - 10)+10;
          let x2 = randomNumber(canvas.width - 10)+10;
          let y2 = randomNumber(canvas.height - 10)+10;
          ctx.moveTo(x1,y1);
          ctx.lineTo(x2,y2);
          ctx.strokeStyle= randomto16Sting();
          ctx.lineWidth=randomNumber(randomRange)+1;
          ctx.stroke();
          ctx.closePath();
      }
      function drawText(dataArray){
          //  ctx.translate(0,0);
          ctx.font = "26px Gaoel";
          ctx.textAlign ='center';
          ctx.textBaseline ='middle';
          let rotateNumber = randomRotate()
          // 以数据的本身作为了旋转点了,需要修正,也就是pre后退一步
          ctx.rotate(-preRotateDeg[1][0])
          ctx.rotate(rotateNumber[0]);
          ctx.fillText(dataArray[0],20,25);
          ctx.fillStyle = randomto16Sting();

          ctx.rotate(-preRotateDeg[1][1])
          ctx.rotate(rotateNumber[1]);
          ctx.fillText(dataArray[1],50,25);
          ctx.fillStyle = randomto16Sting();

          ctx.rotate(-preRotateDeg[1][2])
          ctx.rotate(rotateNumber[2]);
          ctx.fillText(dataArray[2],80,25);
          ctx.fillStyle = randomto16Sting();

          
     }
      function randomOption(inputNode){
       let firstNumber = Math.ceil(Math.random(1)*10);
       let operationNumber = Math.ceil(Math.random()*4);
       let secodeNumber = Math.ceil(Math.random(1)*10);
       let answer = 0;
       let mul = 0; //交互使用
        switch (operationNumber) {
          case 1:
            operation="+";
            answer = firstNumber+secodeNumber;
            break;
            case 2:
            operation="-"
            if(firstNumber < secodeNumber){
              mul = firstNumber ;
              firstNumber = secodeNumber;
              secodeNumber = mul;
            }
            answer = firstNumber - secodeNumber;
            break;
            case 3:
            operation="×"
            answer = firstNumber*secodeNumber;
            break;
            case 4:
            operation="÷"
           let answerFirst = firstNumber*secodeNumber;
           mul = firstNumber ;
           firstNumber = answerFirst;
           answer  = mul;
            break;
          default:
            break;
        }
        
        let dataArray = [firstNumber,operation,secodeNumber,answer];
          inputNode.value =  dataArray[3];
          //清除画布
          ctx.clearRect(0,0,canvas.width,canvas.height)
          for (let index = 0; index < 60; index++) {
            //画球,传画球的半径(范围)
            drawArc(15);
          } 
          
          for (let index = 0; index < 6; index++) {
             //画线,传画线的线宽(范围)
            drawLine(4);
          } 

           //画显示文字
          drawText(dataArray);
      }
    </script>
</body>
</html>
复制代码

效果图:

8.gif

🐯结语

祝你我好运🐮🐯

分类:
前端
标签: