canvas-demo1(滑块验证码)

1. 最终效果

抖音滑块验证码java 滑块验证码实现_抖音滑块验证码java

2.滑块验证码思路

大概思路:设置两个画布,一个为显示图像的canvas画布,一个为拼图的block画布,block画布拼图内容从图像画布中的一部分裁剪得到(使用clip()),通过绑定鼠标拖动事件,滑动滑块得到block最终的落点坐标,如果坐标落点与原剪切点坐标偏差在设置范围内,显示成功,否则显示失败。

3.绘制拼图

所需要绘制的拼图图形:

抖音滑块验证码java 滑块验证码实现_前端_02

首先进行几何分析,主要由1个正方形+两块突出的部分圆+凹陷的圆

明确需要使用到的绘制方法有基本的lineTo绘制线段路径,arc绘制圆形,和globalCompositeOperation异或混合处理图形

为方便演示,单独创建画布进行每一步的操作演示:

const x = 30;//起始点x坐标
  const y = 30;//起始点y坐标
  const l = 42;//正方形边长
  const r = 10;//圆的半径
  const PI = Math.PI;
  1. 拼图左上角开始绘制和第一个圆形部分
ctx.moveTo(x, y);
ctx.lineTo(x + l / 2, y);//绘制边
ctx.arc(x + l / 2, y - r + 2, r, 0, 2 * PI);//绘制上圆
ctx.lineTo(x + l / 2, y);//返回到边的结束点

抖音滑块验证码java 滑块验证码实现_前端_03

抖音滑块验证码java 滑块验证码实现_Math_04

圆心y轴左边为(y - r + 2)中的+2是因为拼图不是完整外凸的圆形,需要一定的纵坐标偏移;

为什么需要第三步ctx.lineTo(x + l / 2, y);

是因为根据arc的定义:

arc(圆心x坐标, 圆心y坐标, 圆的半径r , 开始角度, 结束角度)

开始角度和结束角度定义如下:

抖音滑块验证码java 滑块验证码实现_滑块_05

因此需要从圆点对应的0度开始绘制到结束的同一个点,而下一次绘制需要回到第一条线段的结束点。

  1. 绘制第二个圆
ctx.lineTo(x + l, y);
  ctx.lineTo(x + l, y + l / 2);
  ctx.arc(x + l + r - 2, y + l / 2, r, 0, 2 * PI);
  ctx.lineTo(x + l, y + l / 2);

抖音滑块验证码java 滑块验证码实现_滑块_06


抖音滑块验证码java 滑块验证码实现_滑块_07

思路同绘制第一个圆相同。

  1. 绘制完剩下的正方形
ctx.lineTo(x + l, y + l);
  ctx.lineTo(x, y + l);
  ctx.lineTo(x, y);

抖音滑块验证码java 滑块验证码实现_Math_08

抖音滑块验证码java 滑块验证码实现_前端_09

  1. 通过异或混合处理剩下的缺口
ctx.fill();//填充上述正方形
  ctx.beginPath();//开始新的绘制
  ctx.arc(x, y + l / 2, r, 1.5 * PI, 0.5 * PI);
  ctx.globalCompositeOperation = "xor";//异或处理
  ctx.fill();//

抖音滑块验证码java 滑块验证码实现_Math_10

xor:使用异或操作,对源图像与目标图像进行结合处理效果如下

抖音滑块验证码java 滑块验证码实现_Math_11

4.随机图片和拼图随机起始位置

随机图片由该网站获取:http://picsum.photos

通过基本的随机数字函数调用:

function getRandomNumberByRange(start, end) {
      return Math.round(Math.random() * (end - start) + start);
    }
 function getRandomImgSrc() {
      return (
        "http://picsum.photos/300/150/?image=" + getRandomNumberByRange(0, 100)
      );
    }

拼图随机位置也是通过随机函数限定边界在画布内随机出现

draw() {
        this.x = getRandomNumberByRange(ll + 10, w - (ll + 10));//10为设定的最小空隙,ll是保证滑块到最终位置和初始位置至少不会重叠
        this.y = getRandomNumberByRange(10 + r * 2, h - (ll + 10));
        draw(this.canvasCtx, "fill", this.x, this.y);
        draw(this.blockCtx, "clip", this.x, this.y);
      }