DIY可视化实现UniApp手机滑块验证组件,支持自定义背景图片、成功提示、错误提示、划动提示等。
手机滑块验证组件是一种广泛应用于手机应用、网页等场景的用户验证机制,其主要目的是通过用户的滑动操作来验证用户的真实性和操作意图,从而增强系统的安全性。以下是对手机滑块验证组件的详细介绍:
用户的真实性和操作意图,从而增强系统的安全性。以下是对手机滑块验证组件的详细介绍:
一、工作原理
滑块验证组件的工作原理基于人机交互的思想,通过要求用户在屏幕上滑动指定的滑块到特定位置来完成验证。这一过程结合了用户的视觉和运动能力,使得验证过程更加难以被自动化程序绕过。
二、组成元素
手机滑块验证组件通常包含以下几个关键元素:
滑块:用户需要操作的对象,通常是一个可拖动的图标或图形。
背景图:滑块需要与之匹配或对齐的图像,背景图上通常包含有特定的图案或元素,用于引导用户找到正确的滑动位置。
指示信息:提示用户如何操作的文字或图形信息,如“拖动滑块完成验证”。
验证逻辑:后台的验证算法,用于判断用户的滑动操作是否符合预期,包括滑块的位置、滑动轨迹等。
三、操作流程
显示验证页面:当用户需要进行验证时,系统会生成一张包含滑块和背景图的验证码图片,并显示在用户的手机上。
用户操作:用户用手指按住滑块,并沿着屏幕指示的方向拖动,直到滑块到达背景图的指定位置。
验证结果:系统会根据用户的滑动操作进行验证,如果验证通过,用户将获得验证成功的反馈;如果验证失败,用户需要重新进行验证。
四、优势与应用
手机滑块验证组件具有以下优势:
提高安全性:通过结合用户的视觉和运动能力进行验证,增加了自动化程序绕过的难度。
提升用户体验:相比传统的验证码输入方式,滑块验证更加简单直观,用户只需进行简单的滑动操作即可完成验证。
适用范围广:可以灵活应用于各种手机应用、网页等场景,满足不同的验证需求。
手机滑块验证组件广泛应用于各类网站和移动应用中,如用户注册、登录、重置密码等场景。它通过简单的滑动操作,有效地保护了用户的账户安全,并提升了用户体验。
五、技术实现
手机滑块验证组件的实现通常涉及图像处理、机器学习等技术。系统需要能够生成复杂的背景图、随机放置滑块位置、捕捉用户的滑动轨迹等。同时,还需要具备高效的验证算法,以快速准确地判断用户的滑动操作是否符合预期。
综上所述,手机滑块验证组件是一种简单有效且广泛应用的用户验证机制,它通过结合用户的视觉和运动能力进行验证,提高了系统的安全性和用户体验。
六、UniApp组件库实现
<template> <view class="diy-verify" v-if="initShow" :class="isModal?'diy-verify-modal':''" :id="elid" @touchmove.stop.prevent="stopMoveHandle" :style="verifyWidthStyle"> <view class="diy-verify-close" @tap="close" v-if="isModal"> <u-icon :size="50" color="#fff" name="close"></u-icon> </view> <view class="diy-verify-wrap" v-if="isShow"> <view class="diy-verify-box"> <image class="diy-verify-img" v-if="verifyImg" :src="verifyImg" mode="scaleToFill"></image> <!-- 右侧用来验证的滑块 --> <view class="diy-verify-block-verify" :style="blockVerifyStyle"></view> <!-- 被css操控的滑块 --> <view class="diy-verify-block-move" :style="blockMoveStyle"></view> <view class="diy-verify-tips" v-if="!showBottomVerify"> <text class="diy-verify-tips-text" :style="blockTipsStyle">{{ tips }}</text> </view> <!-- 手指触摸的滑块 --> <view class="diy-verify-block-touch" :style="blockTouchStyle" @touchstart="touchstartHandle" @touchmove="touchmoveHandle" @touchend="touchendHandle"> </view> <view @tap="initVerify()" class="diy-verify-refresh"> <u-icon :size="50" :color="refreshColor" name="reload"></u-icon> </view> </view> <view class="diy-verify-verify" v-if="showBottomVerify" :style="verifyBottomStyle"> <view :class="(isSuccess?'diy-verify-success-text':'diy-verify-text')">{{tips}}</view> <!-- 被css操控的滑块 --> <view class="diy-verify-verify-move" :style="verifyMoveStyle"> <u-icon :size="bottomSize-10" :name="isSuccess?'checkbox-mark':'arrow-right-double'"></u-icon> </view> <!-- 手指触摸的滑块 --> <view class="diy-verify-verify-touch" :style="verifyTouchStyle" @touchstart="touchstartHandle" @touchmove="touchmoveHandle" @touchend="touchendHandle"> </view> </view> </view> </view> </template> <script> export default { name: 'zmmVerifyVerify', emits: ["update:modelValue",'close',"change"], props: { // 通过双向绑定控制组件的弹出与收起 modelValue: { type: Boolean, default: false }, //是否弹窗验证 isModal:{ type:Boolean, default: false }, //提醒 tip: { type: String, default: '请将左侧透明滑块拖进白色框内' }, successTip: { type: String, default: "验证通过" }, failTip:{ type: String, default: "验证失败" }, //滑块大小 verifySize: { type: Number, default: 50 }, //滑块颜色 verifyColor: { type: String, default: 'rgba(0,0,0,0.4)' }, //图片验证高度 verifyHeight:{ type: Number, default: 170 }, // 图片 verifyImg: { type: String, default: '' }, //刷新颜色 refreshColor: { type: String, default: '#ffffff' }, //校验正负差值区间像素 between: { type: Number, default: 10 }, //如果不显示底部滑动条时提示字段颜色 tipColor:{ type: String, default: '#ffff00' }, // 是否显示底部滑动条 showBottomVerify: { type: Boolean, default: false }, //底部滑块大小 bottomSize: { type: Number, default: 40 }, bottomBarColor: { type: String, default: '#eee' }, bottomSuccessColor: { type: String, default: '#19be6b' }, //底部滑块颜色 bottomBgColor: { type: String, default: '#ffffff' }, bottomColor: { type: String, default: '#bbbbbb' }, bottomBorderColor: { type: String, default: '#bbbbbb' }, }, data() { return { elid: this.$u.guid(), verifyWidth:280, initShow:!this.isModal, tips:this.tip, startPageX: 0, //开始距离 moveLeft: 0, //滑动距离 isSuccess: false, //是否成功 autoLeft: 80, //验证滑块随机的像素 autoTop: 80, //验证滑块随机的top像素 isShow: false }; }, watch: { verifyImg: { immediate: true, handler(){ this.init() } } }, computed: { verifyWidthStyle(){ return `--diy-verify-width:${this.verifyWidth}px;--diy-verify-height:${this.verifyHeight}px` }, blockVerifyStyle() { return `top:${this.autoTop}px;left:${this.autoLeft}px;height:${this.verifySize}px;width:${this.verifySize}px;background-color:${this.verifyColor};` }, blockMoveStyle() { let moveLeft = this.isSuccess?this.autoLeft:this.moveLeft; return `top:${this.autoTop}px;left:${moveLeft}px;height:${this.verifySize}px;width:${this.verifySize}px;background-color: ${this.verifyColor};` }, blockTipsStyle(){ return `color:${this.tipColor}` }, blockTouchStyle() { return `top:${this.autoTop}px;height:${this.verifySize}px;width:${this.verifySize}px;` }, verifyMoveStyle() { let moveLeft = this.isSuccess?this.autoLeft:this.moveLeft; return `border:1px solid ${this.bottomBorderColor};left:${moveLeft}px;height:${this.bottomSize}px;width:${this.bottomSize}px;color:${this.bottomColor};background-color: ${this.bottomBgColor};` }, verifyTouchStyle() { return `height:${this.bottomSize}px;width:${this.bottomSize}px;` }, verifyBottomStyle() { return `font-size:${this.bottomSize-26}px;line-height:${this.bottomSize}px;height:${this.bottomSize}px;background-color:${this.isSuccess?this.bottomSuccessColor:this.bottomBarColor}` } }, mounted() { this.init() }, methods: { // 初始化 init() { if(this.initShow){ this.$nextTick(()=>{ this.$uGetRect('#' + this.elid).then((res) => { this.verifyWidth = this.isModal?res.width-20:res.width; this.initVerify() }); }) } }, initVerify(){ this.isShow = true this.moveLeft = 0; this.isSuccess = false; this.autoTop = this.randPostion(0, this.verifyHeight - this.verifySize); this.autoLeft = this.randPostion(this.verifySize + 20, this.verifyWidth - this.verifySize); }, show(){ this.initShow = true; this.init(); }, close(){ if(this.isModal){ this.initShow = false; this.$emit("close"); } }, // 拦截其他触摸事件防止nvue下input等元素层级问题 stopMoveHandle(e) { if (e.preventDefault) { // 阻止页面滚动 e.preventDefault() } }, // 随机数 randPostion(min, max) { //返回包括最大/小值 return Math.floor(Math.random() * (max - min + 1)) + min; }, //按下 touchstartHandle(e) { if (this.isSuccess) { return; } this.startPageX = e.changedTouches[0].pageX; }, // 滑动 touchmoveHandle(e) { // 滑动分两个块来操作不然会有数据抖动 if (this.isSuccess) { return; } var left = e.changedTouches[0].pageX - this.startPageX; //补偿起始位置 this.moveLeft = left; }, // 滑动离开(最终) touchendHandle(e) { var endLeft = e.changedTouches[0].pageX; var verifyLeft = this.autoLeft + this.startPageX; //补偿起始位置 var chazhi = verifyLeft - endLeft; //最终差值 // 判断是否在正负差值区间 if (chazhi >= 0 - this.between && chazhi <= this.between) { this.isSuccess = true; // 通过会执行成功和关闭 this.tips = this.successTip this.$emit("update:modelValue", true); this.$emit("change", true); uni.showToast({ icon:'none', title:this.successTip?this.successTip:'验证通过' }) this.close() } else { this.tips = this.failTip?this.failTip:'验证失败'; setTimeout(()=>{ this.tips = this.tip; }) // 失败会执行失败并重新初始化 this.show(); uni.showToast({ title: this.failTip?this.failTip:'验证失败', icon: 'none' }); this.$emit("update:modelValue", false); this.$emit("change", false); } } } }; </script> <style lang="scss" scoped> .diy-verify { --diy-verify-width:280px; --diy-verify-height:170px; --textColor:#4d4d4d; width: 100%; display: flex; flex-direction: row; align-items: center; justify-content: center; &.diy-verify-modal{ position: fixed; top:0px; left:0px; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.6); z-index: 99999999; transition: all 0.3s ease-in-out 0s; .diy-verify-close{ position: absolute; top:10px; right: 10px; } .diy-verify-wrap { opacity: 1; transition-duration: 0.3s; -ms-transform: scale(1); transform: scale(1); overflow-x: hidden; overflow-y: auto; pointer-events: auto; } } .diy-verify-wrap { display: flex; flex-direction: column; position: relative; margin: 0 auto; width: var(--diy-verify-width); background-color: #ffffff; .diy-verify-tips { position: absolute; left:0; bottom: 0; width: 100%; background: rgba(0, 0, 0, .6); font-size: 12px; line-height: 20px; text-align: center; .diy-verify-tips-text { color: #ff0; } } .diy-verify-refresh{ position: absolute; right: 10px; top: 10px; } .diy-verify-text{ position: absolute; left: 0; top: 0; width: 100%; height: 100%; color: #FFFFFF; text-align: center; background: -webkit-gradient(linear,left top,right top,color-stop(0,var(--textColor)),color-stop(.4,var(--textColor)),color-stop(.5,#fff),color-stop(.6,var(--textColor)),color-stop(1,var(--textColor))); animation: slidetounlock 3s infinite; -webkit-background-clip: text; -webkit-text-fill-color: transparent; } .diy-verify-success-text{ position: absolute; left: 0; top: 0; width: 100%; text-align: center; color: #FFFFFF; } @keyframes slidetounlock{ 0% { background-position: -200rpx 0; } 100% { background-position: 200rpx 0; } } .diy-verify-box { position: relative; width: var(--diy-verify-width); height: var(--diy-verify-height); overflow: hidden; .diy-verify-img { width: var(--diy-verify-width); height: var(--diy-verify-height); border-radius: 0px; } .diy-verify-block-verify, .diy-verify-block-move, .diy-verify-block-touch { position: absolute; left: 0px; top: 0; border-radius: 0px; } .diy-verify-block-verify { border:1px solid #fff; } } .diy-verify-verify { height:40px; width: var(--diy-verify-width); background-color: rgba(0,0,0,0.07); position: relative; overflow: hidden; .diy-verify-verify-move{ display: flex; align-items: center; justify-content: center; } .diy-verify-verify-move, .diy-verify-verify-touch { border-radius: 0px; } .diy-verify-verify-move,.diy-verify-verify-touch { position: absolute; left: 0px; top: 0px; } } } } </style>
七、组件使用
<template> <view class="container container23285"> <view class="diygw-col-24"> <diy-verify v-model="verify" successBarColor="#ff3d0c"></diy-verify> </view> <view class="diygw-col-24"> <diy-verifyimg class="diygw-col-24" v-model="verifyimg" :verifySize="50" verifyImg="/static/pic1.jpg" :showBottomVerify="true"></diy-verifyimg> </view> <view class="clearfix"></view> </view> </template> <script> export default { data() { return { //用户全局信息 userInfo: {}, //页面传参 globalOption: {}, //自定义全局变量 globalData: { logintype: '0', agree: '0' }, verify: false, verifyimg: false }; }, onShow() { this.setCurrentPage(this); }, onLoad(option) { this.setCurrentPage(this); if (option) { this.setData({ globalOption: this.getOption(option) }); } this.init(); }, methods: { async init() {} } }; </script> <style lang="scss" scoped> .container23285 { } </style>