滑动拼图验证

 1.参考地址:https://www.cnblogs.com/huanglei-/p/8568405.html

 2.效果:

 3.逻辑:底部滑块的拖动控制小拼图移动,当其左边距与切割位置x坐标一致,或在一定范围内,即为正确;难点在于切割出来拼图,这一步参考上面链接地址;

4.项目地址:https://gitee.com/beiysd/react-methors.git

5.部分源码如下:

/**
 * @name BlockImgMove
 * @description 滑动拼图验证
 */

import React, { Component } from "react";
import Title from "@/component/Title";
import { Icon, Spin } from "antd";
import { main } from "@/utils/base64";
import styles from "./styles.module.less";
const imgSrc = require("@/assets/img/10355.jpg");
const canWidth = 300; //容器宽
const canHeight = 160; //容器高
const canLitWidth = 42; //图片滑块宽
const canLitR = 10; //滑块附带小圆半径
const canLitL = canLitWidth + canLitR * 2 + 3; //小拼图实际边长
const PI = Math.PI; //圆周率
class BlockImgMove extends Component {
  state = {
    blockX: 0, //小拼图X轴坐标
    textMess: "向右移动拼接图片,完成验证",
    type: false, //验证状态,false为验证失败,true验证成功
    loading: false, //加载状态
    canvasRand: "",
    blockRand: "",
    imgPath: ""
  };
  y = 0; //小拼图达到的Y轴坐标
  x = 0; //小拼图达到的X轴坐标
  img = null;
  componentDidMount() {
    this.onMouseDown();
    this.init();
  }
  init = () => {
    this.y = 0;
    this.x = 0;
    // this.img = null;
    this.setState(
      {
        type: false,
        blockX: 0,
        textMess: "向右移动拼接图片,完成验证",
        canvasRand: `canvas${this.getRandomNumberByRange(0, 100)}`, //"canvasRand", //this.getRandomNumberByRange(0, 100)
        blockRand: `block${this.getRandomNumberByRange(101, 200)}` // "blockRand" //this.getRandomNumberByRange(101, 200)
      },
      () => {
        this.getImg();
      }
    );
  };
  /**
   * @name onMouseDown
   * @description 监听鼠标点击
   */
  onMouseDown = () => {
    let outBox = document.getElementById("out_mouse_img");
    let mouseBox = document.getElementById("mouse_img");
    let that = this;

    mouseBox.onmousedown = function (ev) {
      let ev00 = ev || window.event;
      let px = ev00.pageX; //初始位置,对于整个页面来说,光标点击位置
      let oL = this.offsetLeft; //初始位置,对于有定位的父级,元素边框侧与父级边框侧的距离 初始为0

      mouseBox.onmousemove = function (evs) {
        if (that.state.type) {
          return;
        }
        let ev01 = evs || window.event;
        let px1 = ev01.pageX; //滑动后,当前鼠标所在位置
        let oL1 = px1 - px + oL; //距初始位置移动的距离
        if (oL1 <= 0) {
          oL1 = 0;
        } else if (oL1 > outBox.clientWidth - mouseBox.clientWidth) {
          oL1 = outBox.clientWidth - mouseBox.clientWidth;
        }
        // console.log("oL1===", oL1);
        that.setState({ blockX: oL1 });
      };
      mouseBox.onmouseup = function () {
        that.cancelMove();
      };
    };
  };
  /**
   * @name textChange
   * @description 验证成功,信息变化
   */
  textChange = () => {
    this.setState({ textMess: "验证成功", type: true });
  };
  /**
   * @name cancelMove
   * @description 鼠标离开滑块,滑块停止滑动并复位
   */
  cancelMove = () => {
    let mouseBox = document.getElementById("mouse_img");
    let mouseLeft = mouseBox.offsetLeft;
    mouseBox.onmousemove = null;
    if (mouseLeft !== 0)
      if (mouseLeft === this.x || (mouseLeft <= this.x + 3 && mouseLeft >= this.x - 3)) {
        //验证成功的不可逆操作
        this.onmousemove = null;
        mouseBox.onmousemove = null;
        this.textChange();
      } else {
        this.init();
      }
  };
  /**
   * @name getRandomNumberByRange
   * @description 获取随机数
   */
  getRandomNumberByRange = (start, end) => {
    return Math.round(Math.random() * (end - start) + start);
  };

  /**
   * @name getImg
   * @description 获取在线图片,图片资源与项目地址源不同导致跨域,需处理跨域
   */
  getImg = async () => {
    const url = `/api-online-img/${canWidth}/${canHeight}/?image=${this.getRandomNumberByRange(1, 100)}`;
    try {
      this.setState({ loading: true });
      //不同源图片,这里是先把线上图片转为base64,再进行渲染,时间上稍微多了1-2秒
      main(url, (base64) => {
        //线上图片出错转用本地图片
        this.setState({ imgPath: base64 ? base64 : imgSrc }, () => {
          this.setState({ loading: false });
          this.drawInit();
        });
      });
    } catch (error) {
      console.log("err==", error);
    }
  };
  /**
   * @name draw
   * @description 画图公用方法
   */
  draw = (ctx, x = 0, y = 0, w = 0, operation) => {
    let r = canLitR;
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.arc(x + w / 2, y - r + 2, r, 0.72 * PI, 2.26 * PI);
    ctx.lineTo(x + w, y);
    ctx.arc(x + w + r - 2, y + w / 2, r, 1.21 * PI, 2.78 * PI);
    ctx.lineTo(x + w, y + w);
    ctx.lineTo(x, y + w);
    ctx.arc(x + r - 2, y + w / 2, r + 0.4, 2.76 * PI, 1.24 * PI, true);
    ctx.lineTo(x, y);
    ctx.lineWidth = 2;
    ctx.fillStyle = "rgba(255, 255, 255, 0.7)";
    ctx.strokeStyle = "rgba(255, 255, 255, 0.7)";
    ctx.stroke();
    ctx.globalCompositeOperation = "destination-over";
    operation === "fill" ? ctx.fill() : ctx.clip();
  };
  /**
   * @name drawInit
   * @description 画图前处理
   */
  drawInit = async () => {
    const { canvasRand, blockRand, imgPath } = this.state;
    const mycanvas = document.getElementById(canvasRand);
    const myblock = document.getElementById(blockRand);
    myblock.width = canWidth;//等宽获取整个图片
    const canvas_ctx = mycanvas.getContext("2d");
    const block_ctx = myblock.getContext("2d");
    //清空画布
    canvas_ctx.clearRect(0, 0, canWidth, canHeight);
    block_ctx.clearRect(0, 0, canWidth, canHeight);
    this.img = document.createElement("img"); //创建小图片滑块
    // 随机位置创建拼图形状
    this.x = this.getRandomNumberByRange(canLitL + 10, canWidth - (canLitL + 10));
    this.y = this.getRandomNumberByRange(10 + canLitR * 2, canHeight - (canLitL + 10));
    //渲染图片
    this.img.onload = async () => {
      canvas_ctx.drawImage(this.img, 0, 0, canWidth, canHeight);
      block_ctx.drawImage(this.img, 0, 0, canWidth, canHeight);
      let _y = this.y - canLitR * 2 - 1; //小拼图实际的坐标

      let ImgData = block_ctx.getImageData(this.x - 5, _y - 3, canLitL, canLitL);
      myblock.width = canLitL;//小拼图的宽,隐藏抠图位置图片
      block_ctx.putImageData(ImgData, 0, _y);
    };
    this.img.src = imgPath; //图片路径

    this.draw(canvas_ctx, this.x, this.y, canLitWidth, "fill");
    this.draw(block_ctx, this.x, this.y, canLitWidth, "clip");
  };

  render() {
    const { textMess, type, blockX, loading, canvasRand, blockRand } = this.state;
    return (
      <div>
        <h3>BlockImgMove</h3>
        <div className={styles.block}>
          <Title text="滑动拼图验证" />
          <div>
            <div className={styles.outDiv}>
              <Spin spinning={loading}>
                <div className={styles.outDivNext}>
                  <Icon type="redo" className={styles.redos} onClick={this.init} />
                  <canvas id={canvasRand} className={styles.outDivNoborder}></canvas>
                  <canvas id={blockRand} className={styles.outDivLitBlock} style={{ left: blockX }}></canvas>
                </div>
              </Spin>
            </div>

            {/**滑块 */}
            <div id="out_mouse_img" className={styles.outBkock}>
              <div id="mouse_img" className={styles.moveBkock} style={{ cursor: type ? "default" : null, marginLeft: blockX }} onMouseLeave={this.cancelMove}>
                {type ? <Icon type="check-circle" className={styles.icon_check} /> : <Icon type="arrow-right" className={styles.icon_check} />}
              </div>
              {/**蓝色背景 */}
              <div id="colorbg_img" className={styles.posBkockColor} style={{ width: blockX }}>
                {type && <div style={{ color: "#fff" }}>{textMess}</div>}
              </div>
              {/**默认背景 */}
              <div className={styles.posBkockDefault}>{!type && <div>{blockX === 0 && textMess}</div>}</div>
            </div>
          </div>
        </div>
      </div>
    );
  }
}
export default BlockImgMove;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值