VUE 实现滑块验证 ① 您所在的位置:网站首页 vue图片放大缩小拖动插件 VUE 实现滑块验证 ①

VUE 实现滑块验证 ①

2023-06-20 07:12| 来源: 网络整理| 查看: 265

请添加图片描述

@作者 : SYFStrive

  请添加图片描述

@博客首页 : HomePage 📜: 微信小程序 📌:个人社区(欢迎大佬们加入) 👉:社区链接🔗 📌:觉得文章不错可以点点关注 👉:专栏连接🔗 💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞 请添加图片描述 在这里插入图片描述 相关专栏

👉 VUE专栏(🔥)

目录 V u e j s Vuejs Vuejs滑块图示结构框架  Html 结构  Css 结构  JS 结构  完整代码  实现效果总结

⡖⠒⠒⠒⠤⢄⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸ ⠀⠀⠀⡼⠀⠀⠀⠀ ⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢶⣲⡴⣗⣲⡦⢤⡏⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⠋⠉⠉⠓⠛⠿⢷⣶⣦⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠇⠀⠀⠀⠀⠀⠀⠘⡇⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡞⠀⠀⠀⠀⠀⠀⠀⢰⠇⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⡴⠊⠉⠳⡄⠀⢀⣀⣀⡀⠀⣸⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⢸⠃⠀⠰⠆⣿⡞⠉⠀⠀⠉⠲⡏⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠈⢧⡀⣀⡴⠛⡇⠀⠈⠃⠀⠀⡗⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣱⠃⡴⠙⠢⠤⣀⠤⡾⠁⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⢀⡇⣇⡼⠁⠀⠀⠀⠀⢰⠃⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⣸⢠⣉⣀⡴⠙⠀⠀⠀⣼⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⡏⠀⠈⠁⠀⠀⠀⠀⢀⡇⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⢸⠃⠀⠀⠀⠀⠀⠀⠀⡼⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⣰⠃⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⣀⠤⠚⣶⡀⢠⠄⡰⠃⣠⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⢀⣠⠔⣋⣷⣠⡞⠀⠉⠙⠛⠋⢩⡀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀ ⠀⡏⢴⠋⠁⠀⣸⠁⠀⠀⠀⠀⠀ ⠀⣹⢦⣶⡛⠳⣄⠀⠀⠀⠀⠀ ⠀⠙⣌⠳⣄⠀⡇ 不能 ⡏⠀⠀ ⠈⠳⡌⣦⠀⠀⠀⠀ ⠀⠀⠈⢳⣈⣻⡇ 白嫖 ⢰⣇⣀⡠⠴⢊⡡⠋⠀⠀⠀⠀ ⠀⠀⠀⠀⠳⢿⡇⠀⠀⠀⠀⠀⠀⢸⣻⣶⡶⠊⠁⠀⠀ ⠀⠀⠀⠀⠀⢠⠟⠙⠓⠒⠒⠒⠒⢾⡛⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⣠⠏⠀⣸⠏⠉⠉⠳⣄⠀⠙⢆⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⡰⠃⠀⡴⠃⠀⠀⠀⠀⠈⢦⡀⠈⠳⡄⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⣸⠳⣤⠎⠀⠀⠀⠀⠀⠀⠀⠀⠙⢄⡤⢯⡀⠀⠀⠀⠀⠀⠀ ⠀⠐⡇⠸⡅⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⡆⢳⠀⠀⠀⠀⠀⠀ ⠀⠀⠹⡄⠹⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣇⠸⡆⠀⠀⠀⠀⠀ ⠀⠀⠀⠹⡄⢳⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⡀⣧⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⢹⡤⠳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣷⠚⣆⠀⠀⠀⠀ ⠀⠀⠀⡠⠊⠉⠉⢹⡀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡎⠉⠀⠙⢦⡀⠀ ⠀⠀⠾⠤⠤⠶⠒⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠙⠒⠲⠤⠽

提示:以下是本篇文章正文内容

V u e j s Vuejs Vuejs

简介 : Vue 是一套用于构建用户界面的 渐进式 框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

Canvas参考链接 :https://blog.csdn.net/u01246837

滑块图示

在这里插入图片描述

在这里插入图片描述

结构框架   Html 结构 完成拼图验证 换一张 浏览器版本过低,请升级浏览器 浏览器版本过低,请升级浏览器 拖动滑块完成拼图,进行账号验证   Css 结构 //滑块验证 .slide-authCode-wrap { position: absolute; left: 0; z-index: 110; bottom: 65px; width: 364px; height: 216.5px; padding: 12px 12px 12px 20px; border: 1px solid #eee; box-shadow: 0 0 2px 2px #eee; background-color: #fff; //关闭验证 .close { cursor: pointer; z-index: 100; position: absolute; right: 10px; top: 10px; display: block; width: 20px; height: 20px; line-height: 20px; span { font-size: 20px; &:active { color: #a4a4a4; } } } //箭头 .arrow { display: block; position: absolute; background-image: url("./images/tips.gif"); background-repeat: no-repeat; width: 16px; height: 8px; background-position: 0 -8px; overflow: hidden; bottom: -8px; left: 190px; } //滑块主要容器 .validate-wrap { //提醒区域 .refresh { display: flex; align-items: center; justify-content: space-between; padding-right: 30px; font-family: Helvetica, Tahoma, Arial, "Microsoft YaHei", "微软雅黑", sans-serif; .refresh-text { font-size: 15px; color: #666; } .refresh-icon { cursor: pointer; color: #06c; .icon { display: inline-block; margin-right: 4px; vertical-align: revert; transition: 0.6s linear; } span { font-size: 15px; } } } //滑块组要容器 .slider-main-container { margin-top: 5px; //画布容器Box #captcha { display: flex; justify-content: center; flex-direction: column; /* 小拼图 */ .block { position: absolute; left: 0; top: 0; } /* 滑动条 */ .slider-container { position: relative; margin: 10px auto 0; opacity: 1; font-size: 14px; visibility: visible; width: 364px; height: 40px; line-height: 40px; text-align: center; color: #05a4ea; //滑块Bg .slide-bg { .left { float: left; width: 40px; height: 40px; background: url("./images/slide-left-icon2.png") no-repeat; } .center { background-image: url("./images/slide-center-bg.png"); margin-left: 40px; margin-right: 40px; overflow: hidden; white-space: nowrap; user-select: none; -moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; } .right { width: 40px; height: 40px; background: url("./images/slide-right-icon2.png") no-repeat; position: absolute; right: 0; top: 0; } } /* 拖动遮罩容器 */ .slider-mask { position: absolute; top: 0; left: 0; width: 364px; height: 40px; border-radius: 36px; border: 0px solid #1991fa; background: linear-gradient(#33b5fb, #8fdfff); } /* 拖动块 */ .slider { position: absolute; left: -3px; top: -3px; width: 45px; height: 45px; background: #fff; box-shadow: 0 0 3px rgba(0, 0, 0, 0.3); cursor: pointer; transition: background 0.2s linear; border-radius: 50%; } .slider:hover { background: #aed6ff; color: #06c; } /* 拖动Icon */ .slider-icon { font-size: 25px; font-weight: 700; vertical-align: middle; } } //活动状态CSS /* 滑动条失败态 */ .slider-container-fail { .slider-mask { background: linear-gradient(#ff5e5e, #ffb3b3); } .slider { padding-top: 2px; box-sizing: border-box; } .slider-icon { color: red; } } } } } }   JS 结构 import { computed, getCurrentInstance, onBeforeUnmount, onMounted, reactive, ref, toRefs, } from "vue"; //第三方模块 import throttle from "lodash/throttle"; // 引入节流会防抖插件 import { ElMessage } from "element-plus"; //自定义模块 import { mapActionsFun } from "@/hooks/VueX"; //获取两个值之间的随机数 import { GetRandomNumberByRange } from "@/utils/Random"; // //回去两个值之间的随机数 // function GetRandomNumberByRange(start, end) { // return Math.round(Math.random() * (end - start) + start); // } import GetGlobalData from "@/utils/Global/GetGlobalData"; export default { name: "SliderVerification", emits: ["successCallback", "failCallback"], setup(props, context) { const data = reactive({ ll: 0, slideVerifyStatus: 0, //控制滑块的状态Icon isOpen: false, }); const slidingData = { l: 35, //去掉突出的部分滑块总边长 r: 7.5, //滑块突出的小圆圈半径 w: 364, //canvas宽度 h: 142, //canvas高度 PI: Math.PI, //2PI = 360 ll: 0, //滑块的实际边长(包括突出部分) }; data.ll = computed(() => { return slidingData.l + slidingData.r * 2; //滑块的实际边长(包括突出部分) }); const thisData = { x: 0, y: 0, captcha: null, canvas_bg: null, blockBox: null, img: null, slider_mask: null, slider: null, url: "", iconRotate: null, currentGlobalData: null, }; const ctx = { canvasCtx: null, blockBoxCtx: null, }; const methods = { Refresh: null, close: null, SliderMousedownEvent: null, }; const eventData = { originX: 0, originY: 0, trail: [], isMouseDown: false, }; //#region 生命周期 const currentInstance = getCurrentInstance(); data.currentGlobalData = GetGlobalData(); onMounted(async () => { await GetElement(); await EventGlobal(); }); onBeforeUnmount(() => { data.currentGlobalData.$bus.all.delete("openSlideVerify"); }); //#endregion //#region 封装方法 const GetData = throttle(async () => { try { // Ajax请求 获取图片 thisData.url = ( await mapActionsFun(["getVerifySlideImg"]).getVerifySlideImg() ).url; thisData.img.src = thisData.url; await DrawInitialize(); await InitImg(); } catch (e) { ElMessage({ showClose: true, dangerouslyUseHTMLString: true, type: "error", message: e, duration: 1500, }); } }, 1500); // 获取需要的元素 const GetElement = async () => { thisData.captcha = currentInstance.proxy.$refs.captcha; thisData.canvas_bg = currentInstance.proxy.$refs.canvas_bg; thisData.blockBox = currentInstance.proxy.$refs.blockBox; thisData.img = currentInstance.proxy.$refs.img; thisData.slider_mask = currentInstance.proxy.$refs.slider_mask; thisData.slider = currentInstance.proxy.$refs.slider; thisData.iconRotate = currentInstance.proxy.$refs.iconRotate; // alpha(boolean):表示canvas是否包含一个alpha通道,设为false则浏览器知道背景永远不透明,能加速对于透明场景和图像的绘制。 // willReadFrequently(Boolean):表示是否计划有大量的回读操作,频繁调用getImageData()方法时能节省内存,仅Gecko内核浏览器支持。 // storage(String):声明使用的storage类型,默认为”persistent”。 ctx.canvasCtx = thisData.canvas_bg.getContext("2d", { willReadFrequently: true, }); ctx.blockBoxCtx = thisData.blockBox.getContext("2d", { willReadFrequently: true, }); }; // 绘画方法 const DrawInitialize = () => { thisData.x = GetRandomNumberByRange( data.ll + 10, slidingData.w - (data.ll + 10) ); thisData.y = GetRandomNumberByRange( slidingData.r * 2 + 10, slidingData.h - (data.ll + 10) ); // fill 通过填充路径的内容区域生成实心的图形 // clip 把已经创建的路径转换成裁剪路径。裁剪路径的作用是遮罩。只显示裁剪路径内的区域,裁剪路径外的区域会被隐藏。 // 注意:clip()只能遮罩在这个方法调用之后绘制的图像,如果是clip()方法调用之前绘制的图像,则无法实现遮罩。 Draw(ctx.canvasCtx, "fill", thisData.x, thisData.y); Draw(ctx.blockBoxCtx, "clip", thisData.x, thisData.y); }; // 绘制 2D渲染,渲染方式,坐标(X,Y) function Draw(ctx, operation, x, y) { // ★前提要创建Canvas 否者无法绘画 // 新建一条路径,路径一旦创建成功,图形绘制命令被指向到路径上生成路径 ctx.beginPath(); // 把画笔移动到指定的坐标(x, y)。相当于设置路径的起始点坐标。 ctx.moveTo(x, y); //(绘制一条从当前位置到指定坐标x,y)的直线. 假如 x 60~304 y 25~82 ctx.lineTo(x + slidingData.l / 2, y); // 绘制圆弧 x, y, r, startAngle, endAngle, anticlockwise 六个参数 // 以(x, y)为圆心,以r为半径,从 startAngle弧度开始到endAngle弧度结束。anticlosewise是布尔值,true表示逆时针,false表示顺时针。(默认是顺时针) // 注意1 这里的度数都是弧度 👉 0弧度是指的x轴正方形 // 注意2 arc绘制的坐标是从最开始的位置计算的 ctx.arc( x + slidingData.l / 2, y - slidingData.r, slidingData.r, 0, 2 * slidingData.PI ); // 回到原来起步画圆的位置 ctx.lineTo(x + slidingData.l / 2, y); // 半径 直径是指 L // 向右再走 直径 位置 ctx.lineTo(x + slidingData.l, y); // 向右再走 半径 位置 ctx.lineTo(x + slidingData.l, y + slidingData.l / 2); // arc方法 须从最开始的位置计算 走到最右边 再向右走半径位置 向下走半径位置 ctx.arc( x + slidingData.l + slidingData.r, y + slidingData.l / 2, slidingData.r, 0, 2 * slidingData.PI ); ctx.lineTo(x + slidingData.l, y + slidingData.l / 2); ctx.lineTo(x + slidingData.l, y + slidingData.l); ctx.lineTo(x, y + slidingData.l); ctx.lineTo(x, y); ctx.fillStyle = "#fff"; ctx[operation](); ctx.beginPath(); ctx.arc( x, y + slidingData.l / 2, slidingData.r, 1.5 * slidingData.PI, 0.5 * slidingData.PI ); // 合成 xor属性作用:重叠部分变透明 ctx.globalCompositeOperation = "xor"; ctx.fill(); } // 初始化图像 const InitImg = () => { ctx.canvasCtx.drawImage(thisData.img, 0, 0, slidingData.w, slidingData.h); ctx.blockBoxCtx.drawImage( thisData.img, 0, 0, slidingData.w, slidingData.h ); const y = thisData.y - slidingData.r * 2; //减去突出圆的大小 // 参数 获取那块区域坐标x,y 宽 const imageData = ctx.blockBoxCtx.getImageData( thisData.x, y, data.ll, data.ll ); thisData.blockBox.width = data.ll; // 后面两个参数从哪里放上去 ctx.blockBoxCtx.putImageData(imageData, 0, y); }; // 绑定事件 刷新 let index = ref(0); methods.Refresh = throttle(() => { index.value++; thisData.iconRotate.style.rotate = -360 * index.value + "deg"; Reset(); }, 2000); // 关闭滑块验证 methods.Close = () => { data.isOpen = false; }; // ----------------按着--------------- methods.SliderMousedownEvent = (e) => { eventData.originX = e.x; eventData.originY = e.y; eventData.isMouseDown = true; }; // ----------------拖动--------------- document.addEventListener("mousemove", (e) => { if (!eventData.isMouseDown) return false; const moveY = e.y - eventData.originY; const moveX = e.x - eventData.originX; // 判断时候超出或者小于0 if (moveX = slidingData.w) return false; // 拖动按钮位置 thisData.slider.style.left = moveX + "px"; // 拖动填充滑块位置 const blockLeft = moveX; thisData.blockBox.style.left = blockLeft + "px"; // 遮罩宽长度 thisData.slider_mask.style.width = moveX + 40 + "px"; // 添加位置 eventData.trail.push(moveY); }); // ----------------抬起--------------- document.addEventListener("mouseup", () => { if (!eventData.isMouseDown) return false; else eventData.isMouseDown = false; // 验证位置; const spliced = Verify(); if (spliced) { // 添加成功 data.slideVerifyStatus = 2; SuccessCallback(); methods.Close(); } else { // 添加失败样式 data.slideVerifyStatus = 4; FailCallback(); setTimeout(() => { Reset(); }, 1000); } }); // 清除 function CleanCtx() { ctx.canvasCtx.clearRect(0, 0, slidingData.w, slidingData.h); ctx.blockBoxCtx.clearRect(0, 0, slidingData.w, slidingData.h); thisData.blockBox.width = slidingData.w; } // 重置 async function Reset() { data.slideVerifyStatus = 0; thisData.slider.style.left = 0; thisData.blockBox.style.left = 0; thisData.slider_mask.style.width = 0; await CleanCtx(); await GetData(); } // 验证 function Verify() { const left = parseInt(thisData.blockBox.style.left); return Math.abs(left - thisData.x) if (data.isOpen) return; data.isOpen = boolValue; Reset(); }); } // 成功总事件 function SuccessCallback() { context.emit("successCallback"); } // 失败总事件 function FailCallback() { context.emit("failCallback"); } //#endregion return { ...toRefs(data), ...methods, }; }, };   完整代码 完成拼图验证 换一张 浏览器版本过低,请升级浏览器 浏览器版本过低,请升级浏览器 拖动滑块完成拼图,进行账号验证 import { computed, getCurrentInstance, onBeforeUnmount, onMounted, reactive, ref, toRefs, } from "vue"; //第三方模块 import throttle from "lodash/throttle"; // 引入节流会防抖插件 import { ElMessage } from "element-plus"; //自定义模块 import { mapActionsFun } from "@/hooks/VueX"; //获取两个值之间的随机数 import { GetRandomNumberByRange } from "@/utils/Random"; // //回去两个值之间的随机数 // function GetRandomNumberByRange(start, end) { // return Math.round(Math.random() * (end - start) + start); // } import GetGlobalData from "@/utils/Global/GetGlobalData"; export default { name: "SliderVerification", emits: ["successCallback", "failCallback"], setup(props, context) { const data = reactive({ ll: 0, slideVerifyStatus: 0, //控制滑块的状态Icon isOpen: false, }); const slidingData = { l: 35, //去掉突出的部分滑块总边长 r: 7.5, //滑块突出的小圆圈半径 w: 364, //canvas宽度 h: 142, //canvas高度 PI: Math.PI, //2PI = 360 ll: 0, //滑块的实际边长(包括突出部分) }; data.ll = computed(() => { return slidingData.l + slidingData.r * 2; //滑块的实际边长(包括突出部分) }); const thisData = { x: 0, y: 0, captcha: null, canvas_bg: null, blockBox: null, img: null, slider_mask: null, slider: null, url: "", iconRotate: null, currentGlobalData: null, }; const ctx = { canvasCtx: null, blockBoxCtx: null, }; const methods = { Refresh: null, close: null, SliderMousedownEvent: null, }; const eventData = { originX: 0, originY: 0, trail: [], isMouseDown: false, }; //#region 生命周期 const currentInstance = getCurrentInstance(); data.currentGlobalData = GetGlobalData(); onMounted(async () => { await GetElement(); await EventGlobal(); }); onBeforeUnmount(() => { data.currentGlobalData.$bus.all.delete("openSlideVerify"); }); //#endregion //#region 封装方法 const GetData = throttle(async () => { try { // Ajax请求 获取图片 thisData.url = ( await mapActionsFun(["getVerifySlideImg"]).getVerifySlideImg() ).url; thisData.img.src = thisData.url; await DrawInitialize(); await InitImg(); } catch (e) { ElMessage({ showClose: true, dangerouslyUseHTMLString: true, type: "error", message: e, duration: 1500, }); } }, 1500); // 获取需要的元素 const GetElement = async () => { thisData.captcha = currentInstance.proxy.$refs.captcha; thisData.canvas_bg = currentInstance.proxy.$refs.canvas_bg; thisData.blockBox = currentInstance.proxy.$refs.blockBox; thisData.img = currentInstance.proxy.$refs.img; thisData.slider_mask = currentInstance.proxy.$refs.slider_mask; thisData.slider = currentInstance.proxy.$refs.slider; thisData.iconRotate = currentInstance.proxy.$refs.iconRotate; // alpha(boolean):表示canvas是否包含一个alpha通道,设为false则浏览器知道背景永远不透明,能加速对于透明场景和图像的绘制。 // willReadFrequently(Boolean):表示是否计划有大量的回读操作,频繁调用getImageData()方法时能节省内存,仅Gecko内核浏览器支持。 // storage(String):声明使用的storage类型,默认为”persistent”。 ctx.canvasCtx = thisData.canvas_bg.getContext("2d", { willReadFrequently: true, }); ctx.blockBoxCtx = thisData.blockBox.getContext("2d", { willReadFrequently: true, }); }; // 绘画方法 const DrawInitialize = () => { thisData.x = GetRandomNumberByRange( data.ll + 10, slidingData.w - (data.ll + 10) ); thisData.y = GetRandomNumberByRange( slidingData.r * 2 + 10, slidingData.h - (data.ll + 10) ); // fill 通过填充路径的内容区域生成实心的图形 // clip 把已经创建的路径转换成裁剪路径。裁剪路径的作用是遮罩。只显示裁剪路径内的区域,裁剪路径外的区域会被隐藏。 // 注意:clip()只能遮罩在这个方法调用之后绘制的图像,如果是clip()方法调用之前绘制的图像,则无法实现遮罩。 Draw(ctx.canvasCtx, "fill", thisData.x, thisData.y); Draw(ctx.blockBoxCtx, "clip", thisData.x, thisData.y); }; // 绘制 2D渲染,渲染方式,坐标(X,Y) function Draw(ctx, operation, x, y) { // ★前提要创建Canvas 否者无法绘画 // 新建一条路径,路径一旦创建成功,图形绘制命令被指向到路径上生成路径 ctx.beginPath(); // 把画笔移动到指定的坐标(x, y)。相当于设置路径的起始点坐标。 ctx.moveTo(x, y); //(绘制一条从当前位置到指定坐标x,y)的直线. 假如 x 60~304 y 25~82 ctx.lineTo(x + slidingData.l / 2, y); // 绘制圆弧 x, y, r, startAngle, endAngle, anticlockwise 六个参数 // 以(x, y)为圆心,以r为半径,从 startAngle弧度开始到endAngle弧度结束。anticlosewise是布尔值,true表示逆时针,false表示顺时针。(默认是顺时针) // 注意1 这里的度数都是弧度 👉 0弧度是指的x轴正方形 // 注意2 arc绘制的坐标是从最开始的位置计算的 ctx.arc( x + slidingData.l / 2, y - slidingData.r, slidingData.r, 0, 2 * slidingData.PI ); // 回到原来起步画圆的位置 ctx.lineTo(x + slidingData.l / 2, y); // 半径 直径是指 L // 向右再走 直径 位置 ctx.lineTo(x + slidingData.l, y); // 向右再走 半径 位置 ctx.lineTo(x + slidingData.l, y + slidingData.l / 2); // arc方法 须从最开始的位置计算 走到最右边 再向右走半径位置 向下走半径位置 ctx.arc( x + slidingData.l + slidingData.r, y + slidingData.l / 2, slidingData.r, 0, 2 * slidingData.PI ); ctx.lineTo(x + slidingData.l, y + slidingData.l / 2); ctx.lineTo(x + slidingData.l, y + slidingData.l); ctx.lineTo(x, y + slidingData.l); ctx.lineTo(x, y); ctx.fillStyle = "#fff"; ctx[operation](); ctx.beginPath(); ctx.arc( x, y + slidingData.l / 2, slidingData.r, 1.5 * slidingData.PI, 0.5 * slidingData.PI ); // 合成 xor属性作用:重叠部分变透明 ctx.globalCompositeOperation = "xor"; ctx.fill(); } // 初始化图像 const InitImg = () => { ctx.canvasCtx.drawImage(thisData.img, 0, 0, slidingData.w, slidingData.h); ctx.blockBoxCtx.drawImage( thisData.img, 0, 0, slidingData.w, slidingData.h ); const y = thisData.y - slidingData.r * 2; //减去突出圆的大小 // 参数 获取那块区域坐标x,y 宽 const imageData = ctx.blockBoxCtx.getImageData( thisData.x, y, data.ll, data.ll ); thisData.blockBox.width = data.ll; // 后面两个参数从哪里放上去 ctx.blockBoxCtx.putImageData(imageData, 0, y); }; // 绑定事件 刷新 let index = ref(0); methods.Refresh = throttle(() => { index.value++; thisData.iconRotate.style.rotate = -360 * index.value + "deg"; Reset(); }, 2000); // 关闭滑块验证 methods.Close = () => { data.isOpen = false; }; // ----------------按着--------------- methods.SliderMousedownEvent = (e) => { eventData.originX = e.x; eventData.originY = e.y; eventData.isMouseDown = true; }; // ----------------拖动--------------- document.addEventListener("mousemove", (e) => { if (!eventData.isMouseDown) return false; const moveY = e.y - eventData.originY; const moveX = e.x - eventData.originX; // 判断时候超出或者小于0 if (moveX = slidingData.w) return false; // 拖动按钮位置 thisData.slider.style.left = moveX + "px"; // 拖动填充滑块位置 const blockLeft = moveX; thisData.blockBox.style.left = blockLeft + "px"; // 遮罩宽长度 thisData.slider_mask.style.width = moveX + 40 + "px"; // 添加位置 eventData.trail.push(moveY); }); // ----------------抬起--------------- document.addEventListener("mouseup", () => { if (!eventData.isMouseDown) return false; else eventData.isMouseDown = false; // 验证位置; const spliced = Verify(); if (spliced) { // 添加成功 data.slideVerifyStatus = 2; SuccessCallback(); methods.Close(); } else { // 添加失败样式 data.slideVerifyStatus = 4; FailCallback(); setTimeout(() => { Reset(); }, 1000); } }); // 清除 function CleanCtx() { ctx.canvasCtx.clearRect(0, 0, slidingData.w, slidingData.h); ctx.blockBoxCtx.clearRect(0, 0, slidingData.w, slidingData.h); thisData.blockBox.width = slidingData.w; } // 重置 async function Reset() { data.slideVerifyStatus = 0; thisData.slider.style.left = 0; thisData.blockBox.style.left = 0; thisData.slider_mask.style.width = 0; await CleanCtx(); await GetData(); } // 验证 function Verify() { const left = parseInt(thisData.blockBox.style.left); return Math.abs(left - thisData.x) if (data.isOpen) return; data.isOpen = boolValue; Reset(); }); } // 成功总事件 function SuccessCallback() { context.emit("successCallback"); } // 失败总事件 function FailCallback() { context.emit("failCallback"); } //#endregion return { ...toRefs(data), ...methods, }; }, }; //滑块验证 .slide-authCode-wrap { position: absolute; left: 0; z-index: 110; bottom: 65px; width: 364px; height: 216.5px; padding: 12px 12px 12px 20px; border: 1px solid #eee; box-shadow: 0 0 2px 2px #eee; background-color: #fff; //关闭验证 .close { cursor: pointer; z-index: 100; position: absolute; right: 10px; top: 10px; display: block; width: 20px; height: 20px; line-height: 20px; span { font-size: 20px; &:active { color: #a4a4a4; } } } //箭头 .arrow { display: block; position: absolute; background-image: url("./images/tips.gif"); background-repeat: no-repeat; width: 16px; height: 8px; background-position: 0 -8px; overflow: hidden; bottom: -8px; left: 190px; } //滑块主要容器 .validate-wrap { //提醒区域 .refresh { display: flex; align-items: center; justify-content: space-between; padding-right: 30px; font-family: Helvetica, Tahoma, Arial, "Microsoft YaHei", "微软雅黑", sans-serif; .refresh-text { font-size: 15px; color: #666; } .refresh-icon { cursor: pointer; color: #06c; .icon { display: inline-block; margin-right: 4px; vertical-align: revert; transition: 0.6s linear; } span { font-size: 15px; } } } //滑块组要容器 .slider-main-container { margin-top: 5px; //画布容器Box #captcha { display: flex; justify-content: center; flex-direction: column; /* 小拼图 */ .block { position: absolute; left: 0; top: 0; } /* 滑动条 */ .slider-container { position: relative; margin: 10px auto 0; opacity: 1; font-size: 14px; visibility: visible; width: 364px; height: 40px; line-height: 40px; text-align: center; color: #05a4ea; //滑块Bg .slide-bg { .left { float: left; width: 40px; height: 40px; background: url("./images/slide-left-icon2.png") no-repeat; } .center { background-image: url("./images/slide-center-bg.png"); margin-left: 40px; margin-right: 40px; overflow: hidden; white-space: nowrap; user-select: none; -moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; } .right { width: 40px; height: 40px; background: url("./images/slide-right-icon2.png") no-repeat; position: absolute; right: 0; top: 0; } } /* 拖动遮罩容器 */ .slider-mask { position: absolute; top: 0; left: 0; width: 364px; height: 40px; border-radius: 36px; border: 0px solid #1991fa; background: linear-gradient(#33b5fb, #8fdfff); } /* 拖动块 */ .slider { position: absolute; left: -3px; top: -3px; width: 45px; height: 45px; background: #fff; box-shadow: 0 0 3px rgba(0, 0, 0, 0.3); cursor: pointer; transition: background 0.2s linear; border-radius: 50%; } .slider:hover { background: #aed6ff; color: #06c; } /* 拖动Icon */ .slider-icon { font-size: 25px; font-weight: 700; vertical-align: middle; } } //活动状态CSS /* 滑动条失败态 */ .slider-container-fail { .slider-mask { background: linear-gradient(#ff5e5e, #ffb3b3); } .slider { padding-top: 2px; box-sizing: border-box; } .slider-icon { color: red; } } } } } }   实现效果

在这里插入图片描述

总结

以上是个人学习Vue的相关知识点,一点一滴的记录了下来,有问题请评论区指正,共同进步,这才是我写文章的原因之,如果这篇文章对您有帮助请三连支持一波



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有