vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > vue3滑块检验

如何在vue3中使用滑块检验vue-puzzle-verification

作者:A-超

这篇文章主要介绍了在vue3中使用滑块检验vue-puzzle-verification的相关资料,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

官网链接 https://github.com/he4398/vue-puzzle-verification

因为官网是vue2写的,而我现在的项目是使用vue3,所有我就把官网的源码拉取下来,改成vue3的写法(没有做任何逻辑上变动)

1、进去官网,把源码拉取下来,把下面两个文件直接放在到自己项目中

我把这两个文件放在自己的项目 新建文件 verification 下

2、把源码的 puzzleVerification.vue文件改写成vue3写法(下面代码复制过去覆盖官网文件):

<template>
  <div class="puzzle-container" v-show="isVerificationShow" >
    <div class="puzzle-header">
      <span class="puzzle-header-left">拖动下方滑块完成拼图</span>
      <div>
        <span class="re-btn iconfont icon-shuaxin" @click="refreshImg"></span>
        <span
          class="close-btn iconfont icon-guanbi"
          @click="closeVerificationBox"
        ></span>
      </div>
    </div>
    <div
      :style="'position:relative;overflow:hidden;width:' + dataWidth + 'px;'"
    >
      <div
        :style="
          'position:relative;width:' +
          dataWidth +
          'px;height:' +
          dataHeight +
          'px;'
        "
      >
        <img
          id="scream"
          ref="scream"
          :src="imgRandom"
          :style="'width:' + dataWidth + 'px;height:' + dataHeight + 'px;'"
        />
        <canvas
          id="puzzle-box"
          ref="puzzleBox"
          :width="dataWidth"
          :height="dataHeight"
        ></canvas>
      </div>
      <div
        class="puzzle-lost-box"
        :style="
          'left:' +
          left_Num +
          'px;width:' +
          dataWidth +
          'px;height:' +
          dataHeight +
          'px;'
        "
      >
        <canvas
          id="puzzle-shadow"
          ref="puzzleShadow"
          :width="dataWidth"
          :height="dataHeight"
        ></canvas>
        <canvas
          id="puzzle-lost"
          ref="puzzleLost"
          :width="dataWidth"
          :height="dataHeight"
        ></canvas>
      </div>
      <p
        :class="'ver-tips' + (displayTips ? ' slider-tips' : '')"
        ref="verTips"
      >
        <template v-if="verification">
          <i style="background-position: -4px -1207px"></i>
          <span style="color: #42ca6b">验证通过</span>
          <span></span>
        </template>
        <template v-if="!verification">
          <i style="background-position: -4px -1229px"></i>
          <span style="color: red">验证失败:</span>
          <span style="margin-left: 4px">拖动滑块将悬浮图像正确拼合</span>
        </template>
      </p>
    </div>
    <div class="slider-container" :style="'width:' + dataWidth + 'px;'">
      <div class="slider-bar"></div>
      <div
        class="slider-btn"
        ref="sliderBtn"
        @mousedown="startMove"
        @touchstart="startMove"
      ></div>
    </div>
  </div>
</template>
<script setup lang="ts">
import {
  ref,
  defineEmits,
  defineProps,
  onBeforeMount,
  onMounted,
  watch,
  nextTick,
} from "vue";
const emits = defineEmits(["update:modelValue"]);
const props = defineProps({
  // 画布图片的尺寸
  width: {
    type: [String, Number],
    default: 260,
  },
  height: {
    type: [String, Number],
    default: 120,
  },
  // 图集
  puzzleImgList: {
    type: Array,
    default: () => [
      require("../assets/thumbnail-img01.jpg"),
      require("../assets/thumbnail-img02.jpg"),
      require("../assets/thumbnail-img03.jpg"),
    ],
  },
  // 滑块的大小
  blockSize: {
    type: [String, Number],
    default: 40,
  },
  // 误差
  deviation: {
    type: [String, Number],
    default: 4,
  },
  // 滑块圆角的大小(仅当其形状是square有效)
  blockRadius: {
    type: [String, Number],
    default: 4,
  },
  // 滑块随机出现的范围
  wraperPadding: {
    type: [String, Number],
    default: 20,
  },
  // 滑块形状 square  puzzle
  blockType: {
    type: String,
    default: "square",
  },
  // 成功的回调
  onSuccess: {
    type: Function,
    default: () => {
      console.log("成功");
    },
  },
  // 失败的回调
  onError: {
    type: Function,
    default: () => {
      console.log("失败");
    },
  },
  verificationShow: {
    type: Boolean,
    default: false,
  },
  modelValue: {
    type: Boolean,
    default: false
  },
});
const isVerificationShow = ref(true);
const moveStart = ref<any>("");
const displayTips = ref(false);
const verification = ref(false);
const randomX = ref(0);
const randomY = ref(0);
const imgRandom = ref<any>("");
const left_Num = ref(0);
const dataWidth = ref(0);
const dataHeight = ref(0);
const puzzleSize = ref(0); // 滑块的大小
const deviationDate = ref(0);
const radiusData = ref(0);
const paddingDate = ref(0);
const puzzleBox = ref<any>(null); //元素标识
const puzzleLost = ref<any>(null); //元素标识
const puzzleShadow = ref<any>(null); //元素标识
const sliderBtn = ref<any>(null); //元素标识
/* 关闭验证 */
const closeVerificationBox = () => {
  isVerificationShow.value = false;
};
/* 刷新 */
const refreshImg = () => {
  let imgRandomIndex = Math.round(
    Math.random() * (props.puzzleImgList.length - 1)
  );
  imgRandom.value = props.puzzleImgList[imgRandomIndex];
  initCanvas();
};
/* 画布初始化 */
const initCanvas = () => {
  clearCanvas();
  let w = dataWidth.value;
  let h = dataHeight.value;
  let PL_Size = puzzleSize.value;
  let padding = paddingDate.value;
  let MinN_X = padding + PL_Size;
  let MaxN_X = w - padding - PL_Size - PL_Size / 6;
  let MaxN_Y = padding;
  let MinN_Y = h - padding - PL_Size - PL_Size / 6;
  randomX.value = Math.round(Math.random() * (MaxN_X - PL_Size) + MinN_X);
  randomY.value = Math.round(Math.random() * MaxN_Y + MinN_Y);
  let X = randomX.value;
  let Y = randomY.value;
  left_Num.value = -X + 10;
  let d = PL_Size / 3;
  let radius = Number(radiusData.value);
  let c = puzzleBox.value;
  let c_l = puzzleLost.value;
  let c_s = puzzleShadow.value;
  let ctx = c.getContext("2d");
  let ctx_l = c_l.getContext("2d");
  let ctx_s = c_s.getContext("2d");
  ctx.globalCompositeOperation = "xor";
  ctx.shadowBlur = 10;
  ctx.shadowColor = "#fff";
  ctx.shadowOffsetX = 3;
  ctx.shadowOffsetY = 3;
  ctx.fillStyle = "rgba(0,0,0,0.7)";
  ctx.beginPath();
  ctx.lineWidth = "1";
  ctx.strokeStyle = "rgba(0,0,0,0)";
  if (props.blockType === "square") {
    ctx.arc(X + radius, Y + radius, radius, Math.PI, (Math.PI * 3) / 2);
    ctx.lineTo(PL_Size - radius + X, Y);
    ctx.arc(
      PL_Size - radius + X,
      radius + Y,
      radius,
      (Math.PI * 3) / 2,
      Math.PI * 2
    );
    ctx.lineTo(PL_Size + X, PL_Size + Y - radius);
    ctx.arc(
      PL_Size - radius + X,
      PL_Size - radius + Y,
      radius,
      0,
      (Math.PI * 1) / 2
    );
    ctx.lineTo(radius + X, PL_Size + Y);
    ctx.arc(
      radius + X,
      PL_Size - radius + Y,
      radius,
      (Math.PI * 1) / 2,
      Math.PI
    );
  } else {
    ctx.moveTo(X, Y);
    ctx.lineTo(X + d, Y);
    ctx.bezierCurveTo(X + d, Y - d, X + 2 * d, Y - d, X + 2 * d, Y);
    ctx.lineTo(X + 3 * d, Y);
    ctx.lineTo(X + 3 * d, Y + d);
    ctx.bezierCurveTo(
      X + 2 * d,
      Y + d,
      X + 2 * d,
      Y + 2 * d,
      X + 3 * d,
      Y + 2 * d
    );
    ctx.lineTo(X + 3 * d, Y + 3 * d);
    ctx.lineTo(X, Y + 3 * d);
  }
  ctx.closePath();
  ctx.stroke();
  ctx.fill();
  let img = new Image();
  img.src = imgRandom.value;
  img.onload = function () {
    ctx_l.drawImage(img, 0, 0, w, h);
  };
  ctx_l.beginPath();
  ctx_l.strokeStyle = "rgba(0,0,0,0)";
  if (props.blockType === "square") {
    ctx_l.arc(X + radius, Y + radius, radius, Math.PI, (Math.PI * 3) / 2);
    ctx_l.lineTo(PL_Size - radius + X, Y);
    ctx_l.arc(
      PL_Size - radius + X,
      radius + Y,
      radius,
      (Math.PI * 3) / 2,
      Math.PI * 2
    );
    ctx_l.lineTo(PL_Size + X, PL_Size + Y - radius);
    ctx_l.arc(
      PL_Size - radius + X,
      PL_Size - radius + Y,
      radius,
      0,
      (Math.PI * 1) / 2
    );
    ctx_l.lineTo(radius + X, PL_Size + Y);
    ctx_l.arc(
      radius + X,
      PL_Size - radius + Y,
      radius,
      (Math.PI * 1) / 2,
      Math.PI
    );
  } else {
    ctx_l.moveTo(X, Y);
    ctx_l.lineTo(X + d, Y);
    ctx_l.bezierCurveTo(X + d, Y - d, X + 2 * d, Y - d, X + 2 * d, Y);
    ctx_l.lineTo(X + 3 * d, Y);
    ctx_l.lineTo(X + 3 * d, Y + d);
    ctx_l.bezierCurveTo(
      X + 2 * d,
      Y + d,
      X + 2 * d,
      Y + 2 * d,
      X + 3 * d,
      Y + 2 * d
    );
    ctx_l.lineTo(X + 3 * d, Y + 3 * d);
    ctx_l.lineTo(X, Y + 3 * d);
  }
  ctx_l.closePath();
  ctx_l.stroke();
  ctx_l.shadowBlur = 10;
  ctx_l.shadowColor = "black";
  ctx_l.clip();
  ctx_s.beginPath();
  ctx_s.lineWidth = "1";
  ctx_s.strokeStyle = "rgba(0,0,0,0)";
  if (props.blockType === "square") {
    ctx_s.arc(X + radius, Y + radius, radius, Math.PI, (Math.PI * 3) / 2);
    ctx_s.lineTo(PL_Size - radius + X, Y);
    ctx_s.arc(
      PL_Size - radius + X,
      radius + Y,
      radius,
      (Math.PI * 3) / 2,
      Math.PI * 2
    );
    ctx_s.lineTo(PL_Size + X, PL_Size + Y - radius);
    ctx_s.arc(
      PL_Size - radius + X,
      PL_Size - radius + Y,
      radius,
      0,
      (Math.PI * 1) / 2
    );
    ctx_s.lineTo(radius + X, PL_Size + Y);
    ctx_s.arc(
      radius + X,
      PL_Size - radius + Y,
      radius,
      (Math.PI * 1) / 2,
      Math.PI
    );
  } else {
    ctx_s.moveTo(X, Y);
    ctx_s.lineTo(X + d, Y);
    ctx_s.bezierCurveTo(X + d, Y - d, X + 2 * d, Y - d, X + 2 * d, Y);
    ctx_s.lineTo(X + 3 * d, Y);
    ctx_s.lineTo(X + 3 * d, Y + d);
    ctx_s.bezierCurveTo(
      X + 2 * d,
      Y + d,
      X + 2 * d,
      Y + 2 * d,
      X + 3 * d,
      Y + 2 * d
    );
    ctx_s.lineTo(X + 3 * d, Y + 3 * d);
    ctx_s.lineTo(X, Y + 3 * d);
  }
  ctx_s.closePath();
  ctx_s.stroke();
  ctx_s.shadowBlur = 20;
  ctx_s.shadowColor = "black";
  ctx_s.fill();
};
/* 通过重置画布尺寸清空画布,这种方式更彻底 */
const clearCanvas = () => {
  let c = puzzleBox.value;
  let c_l = puzzleLost.value;
  let c_s = puzzleShadow.value;
  c.setAttribute("height", c.getAttribute("height"));
  c_l.setAttribute("height", c.getAttribute("height"));
  c_s.setAttribute("height", c.getAttribute("height"));
};
/* 按住滑块后初始化移动监听,记录初始位置 */
const startMove = (e:any) => {
  // console.log(e);
  e = e || window.event;
  sliderBtn.value.style.backgroundPosition = "0 -216px";
  moveStart.value = e.pageX || e.targetTouches[0].pageX;
  addMouseMoveListener();
};
/* 滑块移动 */
const moving = (e:any) => {
  e = e || window.event;
  let moveX = e.pageX || e.targetTouches[0].pageX;
  let d = moveX - moveStart.value;
  let w = dataWidth.value;
  let PL_Size = puzzleSize.value;
  let padding = paddingDate.value;
  if (moveStart.value === "") {
    return "";
  }
  if (d < 0 || d > w - padding - PL_Size) {
    return "";
  }
  sliderBtn.value.style.left = d + "px";
  sliderBtn.value.style.transition = "inherit";
  puzzleLost.value.style.left = d + "px";
  puzzleLost.value.style.transition = "inherit";
  puzzleShadow.value.style.left = d + "px";
  puzzleShadow.value.style.transition = "inherit";
};
/* 移动结束,验证并回调 */
const moveEnd = (e:any) => {
  e = e || window.event;
  let moveEnd_X = (e.pageX || e.changedTouches[0].pageX) - moveStart.value;
  let ver_Num = randomX.value - 10;
  let deviationValue = deviationDate.value;
  let Min_left = ver_Num - deviationValue;
  let Max_left = ver_Num + deviationValue;
  if (moveStart.value !== "") {
    if (Max_left > moveEnd_X && moveEnd_X > Min_left) {
      displayTips.value = true;
      verification.value = true;
      setTimeout(function () {
        displayTips.value = false;
        initCanvas();
        /* 成功的回调函数 */
        props.onSuccess();
      }, 500);
    } else {
      displayTips.value = true;
      verification.value = false;
      setTimeout(function () {
        displayTips.value = false;
        initCanvas();
        /* 失败的回调函数 */
        props.onError();
      }, 800);
    }
  }
  if (
    typeof sliderBtn.value !== "undefined" &&
    typeof puzzleLost.value !== "undefined" &&
    typeof puzzleShadow.value !== "undefined"
  ) {
    setTimeout(function () {
      sliderBtn.value.style.left = 0;
      sliderBtn.value.style.transition = "left 0.5s";
      puzzleLost.value.style.left = 0;
      puzzleLost.value.style.transition = "left 0.5s";
      puzzleShadow.value.style.left = 0;
      puzzleShadow.value.style.transition = "left 0.5s";
    }, 400);
    sliderBtn.value.style.backgroundPosition = "0 -84px";
  }
  moveStart.value = "";
};
/* 全局绑定滑块移动与滑动结束,移动过程中鼠标可在页面任何位置 */
const addMouseMoveListener = () => {
  document.addEventListener("mousemove", moving);
  document.addEventListener("touchmove", moving);
  document.addEventListener("mouseup", moveEnd);
  document.addEventListener("touchend", moveEnd);
};
watch(
  //监控数据变化
  () => isVerificationShow.value,
  (newVal, _d) => {
    emits("update:modelValue", newVal);
  },
  { immediate: true }
);
watch(
  () => props.modelValue,
  (val) => {
    isVerificationShow.value = val;
  }
)
watch(
  //监控数据变化
  () => props.verificationShow,
  (newVal, _d) => {
    isVerificationShow.value = newVal;
  }
);
onBeforeMount(() => {
  // 随机显示一张图片
  let imgRandomIndex = Math.round(
    Math.random() * (props.puzzleImgList.length - 1)
  );
  imgRandom.value = props.puzzleImgList[imgRandomIndex];
  puzzleSize.value = Number(props.blockSize);
  deviationDate.value = Number(props.deviation);
  radiusData.value = Number(props.blockRadius);
  dataWidth.value = Number(props.width);
  dataHeight.value = Number(props.height);
  paddingDate.value = Number(props.wraperPadding) || 20;
});
onMounted(() => {
  nextTick(() => {
    initCanvas();
  });
});
</script>
<style scoped>
.slider-btn {
  position: absolute;
  width: 44px;
  height: 44px;
  left: 0;
  top: -7px;
  z-index: 12;
  cursor: pointer;
  background-image: url(../assets/sprite.3.2.0.png);
  background-position: 0 -84px;
  transition: inherit;
}
.ver-tips {
  position: absolute;
  left: 0;
  bottom: -22px;
  background: rgba(255, 255, 255, 0.9);
  height: 22px;
  line-height: 22px;
  font-size: 12px;
  width: 100%;
  margin: 0;
  text-align: left;
  padding: 0 8px;
  transition: all 0.4s;
}
.slider-tips {
  bottom: 0;
}
.ver-tips i {
  display: inline-block;
  width: 22px;
  height: 22px;
  vertical-align: top;
  background-image: url(../assets/sprite.3.2.0.png);
  background-position: -4px -1229px;
}
.ver-tips span {
  display: inline-block;
  vertical-align: top;
  line-height: 22px;
  color: #455;
}
.active-tips {
  display: block;
}
.hidden {
  display: none;
}
.puzzle-container {
  position: relative;
  display: inline-block;
  padding: 15px 15px 28px;
  border: 1px solid #ddd;
  background: #ffffff;
  border-radius: 16px;
}
.puzzle-header {
  display: flex;
  justify-content: space-between;
  margin: 5px 0;
}
.puzzle-header-left {
  color: #333;
}
.re-btn,
.close-btn {
  font-size: 16px;
  cursor: pointer;
  color: #666;
}
.re-btn:hover {
  color: #67c23a;
}
.close-btn:hover {
  color: #f56c6c;
}
.close-btn {
  margin-left: 5px;
}
.slider-container {
  position: relative;
  margin: 10px auto 0;
  min-height: 15px;
}
.slider-bar {
  height: 10px;
  border: 1px solid #c3c3c3;
  border-radius: 5px;
  background: #e4e4e4;
  box-shadow: 0 1px 1px rgba(12, 10, 10, 0.2) inset;
  position: absolute;
  width: 100%;
  top: 7px;
}
#puzzle-box {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 22;
}
#puzzle-shadow {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 22;
}
#puzzle-lost {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 33;
}
.puzzle-lost-box {
  position: absolute;
  width: 260px;
  height: 116px;
  left: 0;
  top: 0;
  z-index: 111;
}
@font-face {
  font-family: "iconfont";
  src: url("../assets/icon-font/iconfont.eot?t=1565160368550"); /* IE9 */
  src: url("../assets/icon-font/iconfont.eot?t=1565160368550#iefix")
      format("embedded-opentype"),
    /* IE6-IE8 */
      url("data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAANUAAsAAAAAByQAAAMIAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCCfAqCFIIkATYCJAMMCwgABCAFhG0HOhtkBsi+QDw2JxWVhBKVFukepjkT9iz2vr6/fAq8RxAPz4+9O/e9O9WkGj15Fk3LQ/NCJ41QaVASVSzTSJ7E2p//a/4CvASvALwTjbaVddq9o71hlfDegDawAW//1zQFGqAwEaokh6yK8jD/9/c4lxuw3PPkdI3ZegseIJGz/1WKn5AESUiaSApHHzJ2/2uCAT/S0Iz7q0i8eCKrBLgEQJauMPX/uZzeKPH5KSuXuSYd9aI4DiigvbFNViAJeovsrkAs8jyBdmME8ODibgRNyZwWiIdGO9DM+KXkU61Cc8XaFI9p0loe6Q3gNvh+fKdFk6KpMuceX5/7cPTJ/Tfy4spDEIxnBb+JihVAEueV9kOVKL4Ca1dZq6YAKXljibz/PCFRiGZmcSdYIInCJw6Toid+iM6+oYJmd5A8BW4pefDQFKWzc2SkXpr+P1LN9+L/JeF4UsZPUGWa/nUPMypPfme7Do2j785+zvL4Z37s1l/wcAEwhNPJqPMcHgerCdZ/wbrLlYRrg2tSMZ45fpHJLQ7humLaL9WW9r64yBa+7D3UcBOs/3jDHKz7+MTzgPkExVfzZnHa3qveHHJTfVjsFjh5X99QxlP8cdhYHOyY+Fmy4BUXoLG2ngiSbe2xv/AbW/8ptNb3/tiQ4MN+w2saBeoEoFUBLHjnAmBTaiLTUFPhO3zDUa2nvAqYK7g0MN39vv3VQzeXDK2GEihajELVagZN8go06bAKzVptQ7tlzuYOAyoqItuwZIgg9LpC0e05VL1eaJI/0GTYD5r1RgXaXcb2nh3mwiydMjlCPrpPaLwkVrZJlsLSAekidDgtCigT4tyEsCcp+dQlxcRjLMjvdV9EoeIkwgt0GYVhgiknc/KkPRNJdzpyXPWmtpdEsGQfIw5BfMj3BGU8iZjyOoulwucHiFYIObiBUGWfICxnekcqiQJAL+UxiHAt11Td0/pqhIJiLBFBNrKS0IonUKl61BzxiLa0RzS1QyatYqi8Pb8yer5d0M7co0aJGqn5TuHErmnksyD2aGIA")
      format("woff2"),
    url("../assets/icon-font/iconfont.woff?t=1565160368550") format("woff"),
    url("../assets/icon-font/iconfont.ttf?t=1565160368550") format("truetype"),
    /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
      url("../assets/icon-font/iconfont.svg?t=1565160368550#iconfont")
      format("svg"); /* iOS 4.1- */
}
.iconfont {
  font-family: "iconfont" !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
.icon-guanbi:before {
  content: "\f01f1";
}
.icon-shuaxin:before {
  content: "\e609";
}
</style>

3、在需要使用的地方直接使用(具体参数参考官网):

<template>
  <PuzzleVerification
    v-model="isVerificationShow1"
    :onSuccess="handleSuccess"
    :onError="handleError"
    blockType="puzzle"
  /> 
</template>
<script setup lang="ts">
import { ref, onMounted, watch } from "vue";
import PuzzleVerification from "@/verification/components/puzzleVerification.vue";
const isVerificationShow1 = ref(true);
const handleSuccess = () => {
  console.log("成功");
  isVerificationShow1.value = true;
};
const handleError = () => {
  console.log("handleError");
  isVerificationShow1.value = true;
};
</script>
<style scoped></style>

到此这篇关于在vue3中使用滑块检验vue-puzzle-verification的文章就介绍到这了,更多相关vue3滑块检验vue-puzzle-verification内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文