canvas实现九宫格连线登录

42 阅读2分钟

1.创建canvas画布

function calcCirclePos(){
  let size = 300;
  let padding = 300 * 0.08;
  // 去除画布padding之外的内容宽高
  const contentSize = size - padding * 2;
  // 除去圆与圆之间的距离
  // 规定每个小圆的直径是总宽度的24%
  const circleWidth = contentSize * 0.24;
  // 每两个圆圈的圆心之间的距离,横竖都一样
  const distance = (contentSize - circleWidth) / 2;
  // 左上角第一个圆的圆心坐标,x和y都一样
  const firstPoint = mathFf(circleWidth / 2);
  // 综上,第一行三个圆的x轴坐标如下
  const xy = [
    firstPoint,
    mathFf(firstPoint + distance),
    mathFf(firstPoint + distance * 2)
  ];
  // 由于横竖每个圆之间的间隔都是一样的,
  // 所以很容易想到,通过以上三个值遍历就可以得出9个圆的圆心
  const points = [];
  let i = 0;
  while (i < 3) {
    for (let index = 0; index < xy.length; index++) {
      const element = xy[index];
      points.push({ x: element, y: xy[i] });
    }
    i++;
  }
  return {
    points: points.map((item) => {
      return {
        x: mathFf(item.x + padding),
        y: mathFf(item.y + padding)
      };
    }),
    circleWidth
  };
}
function initCanvas(){
  let canvas = document.getElementById("lockCanvas");
  canvas.width = 300;
  canvas.height = 300;
  let ctx  = canvas.getContext("2d");
  ctx.fillStyle = "#fff";
  ctx.fillRect(0, 0, 300, 300);
  let data = calcCirclePos();
  points.value = data.points.map((item,index)=>{
    return{
      x:item.x,
      y:item.y,
      k:index + 1
    }
  });
  circleWidth.value = data.circleWidth;
  //画圆
  drawCircle(ctx)
  //时间绑定
  bindEvent(canvas, ctx);
}

2.画九宫格的九个小圆

//绘制小圆
function drawCircle(ctx ){
  // 循环绘制9个圆
  points.value.forEach((item)=>{
    // 每一次都要重新开始新路径
    ctx.beginPath();
    ctx.arc(item.x, item.y, circleWidth.value / 2, 0, Math.PI * 2);
    ctx.closePath();
    // 将线条颜色设置为蓝色
    ctx.strokeStyle = "#217bfb";
    // stroke() 方法默认颜色是黑色(如果没有上面一行,则会是黑色)
    ctx.stroke();
  })
}

3.事件绑定

//获取触摸坐标
function getTouchPosition(canvas, event){
  const rect = canvas.getBoundingClientRect();
  const x = event.pageX - rect.left;
  const y = event.pageY - rect.top;
  return { x, y };
}
​
//监听手势
function bindEvent(canvas, ctx){
// 判断设备
  const isMobile = /Mobile|Android/i.test(navigator.userAgent);
  if(isMobile){
    // 监听触摸开始事件
    canvas.addEventListener("touchstart", function (e) {
      // 这里要判断一下是几指触摸,只允许单指触摸
      if (e.touches.length !== 1) return;
      // 获取触摸的坐标位置
      const { x, y } = getTouchPosition(canvas, e.touches[0]);
      // 判断是否滑动到了圆圈内,是就返回圆的坐标
      const point = trigger(x, y);
      if (!point) {
        // 没有返回坐标,就说明没有滑到任何一个小圆内,就不用管
        return
      }
      // 把被触发的小圆坐标存起来
      hitPoints.value.push(point);
      // 绘制触发后的样式和连线
      drawHitCircle(canvas,ctx);
    }, false);
​
    // 监听触摸移动事件
    canvas.addEventListener("touchmove", function (e) {
    // 防止页面跟着移动
    e.preventDefault();
    if (e.touches.length !== 1) return;
      const { x, y } = getTouchPosition(canvas, e.touches[0]);
      const point = trigger(x, y);
      if (!point) {
        // 没有返回坐标,就说明没有滑到任何一个小圆内,就不用管
        return
      }
      if (hitPoints.value.includes(point)) {
        // 如果那个位置已被命中过了,就不管
        return
      }
      // 把被触发的小圆坐标存起来
      hitPoints.value.push(point);
      // 绘制触发后的样式和连线
      drawHitCircle(canvas,ctx);
    }, false);
​
    // 监听触摸结束事件
    canvas.addEventListener("touchend", function (e) {
      if (hitPoints.value.length < 4) {
        hitPoints.value = [];
        // 重新绘制
        drawHitCircle(canvas,ctx);
        return showFailToast('密码无效,至少需要四个点')
      } else {
​
        let temp = hitPoints.value.map((item)=>{
          return item.k
        })
        let count = '';
        temp.forEach((el)=>{
          count += el;
        })
        state.gesturePassword = count;
        // 密码有效将密码传给后端或存起来
        GestureLogin(state.gesturePassword)
​
        // 然后清空临时存储的点
       hitPoints.value = [];
        // 重新绘制
        drawHitCircle(canvas,ctx);
      }
​
    }, false);
  }
}