引言
在实际项目应用开发中,为了防止用户频繁发起登录请求,导致后端登录访问压力瞬时过大,我们可以设计一些验证规则防刷,最常见的方式是通过输入验证码的方式降低刷新频率,后台通过返回不同的验证码从而降低用户的频繁请求,这里我们提供一种前端的方式,降低防刷的频率,那就是滑块验证码验证。话不多说,先上效果图。
本组件的集成及案例教程依然在我们的ATP应用测试平台中,源码地址:atp: 应用测试平台,仅供参考。滑块组件使用vue+element实现,希望你对vue中的常用功能有所了解,关于父子组件传值,watch、computed等监控的了解。
正文
创建滑块验证组件slider-verify-code.vue<template><div class="drag" :style="style"><div class="background"/><div class="text shadow" onselectstart="return false" :style="{ color: textColor }"><slot name="content">{{ content }}</slot></div><div class="slider" :style="{height,width:sliderWidth}"><slot v-if="icon" name="icon"><i :class="icon"></i></slot><slot v-else name="icon">>></slot></div></div></template><script>const debounce = (function () {let timer = 0return function (callback, ms) {clearTimeout(timer)timer = setTimeout(callback, ms)}})();export default {name: 'slider-verify-code',model: {event: 'change',prop: 'isLock'},props: {isLock: { //解锁状态type: [String, Boolean, Number, Object],required: true,default: false},icon: { //滑块图标type: [String],default: "el-icon-d-arrow-right"},activeValue: { //滑块解锁后的值type: [String, Boolean, Number, Object],default: true},inactiveValue: { //滑块解锁前的值type: [String, Boolean, Number, Object],default: false},content: { //滑块的文字type: [String],default: "请向右拖动滑块"},height: { //高度type: [String],default: "40px"},sliderWidth: { //滑块宽度type: [String],default: "40px"},background: { //高度type: [String],default: "#e8e8e8"},textColor: { //滑块的文字颜色type: [String],default: "#777"}},watch: {isLock(data) { //重置样式!data && this.init();},},computed: {style() {const {height, background} = this;return {height, 'line-height': height, background};},resize() {return document.body.clientWidth;},},mounted() {this.init();window.onresize = () => {debounce(() => {this.init();}, 120);};},methods: {/*** 定义一个获取DOM元素的方法-选择器*/selector(selector) {return document.querySelector(selector);},/*** 初始化*/init() {const box = this.selector('.drag'); //容器const background = this.selector('.background'); //背景const text = this.selector('.text'); //文字const slider = this.selector('.slider');//滑块const distance = box.offsetWidth - slider.offsetWidth;//滑动成功的宽度(距离)let success = this.inactiveValue;//是否通过验证的标志// 初始化的时候 清除所有属性slider.style.transition = null;background.style.transition = null;slider.style.left = 0 + 'px';background.style.width = 0 + 'px';text.innerHTML = this.content;slider.innerHTML = '<i class="el-icon-d-arrow-right"></i>';slider.style.color = '#777';//二、给滑块注册鼠标按下事件slider.onmousedown = (event) => {//1.鼠标按下之前必须清除掉后面设置的过渡属性slider.style.transition = null;background.style.transition = null;//说明:clientX 事件属性会返回当事件被触发时,鼠标指针向对于浏览器页面(或客户区)的水平坐标。//2.当滑块位于初始位置时,得到鼠标按下时的水平位置const ev = event || window.event;const downX = ev.clientX;//三、给文档注册鼠标移动事件document.onmousemove = (e) => {const evt = e || window.event;//是为了更好的兼容IE浏览器和非ie浏览器。在ie浏览器中,window.event是全局变量,在非ie中,就需要自己传入一个参数来获取event啦,所以就有了var e = e||window.event//1.获取鼠标移动后的水平位置const moveX = evt.clientX;//2.得到鼠标水平位置的偏移量(鼠标移动时的位置 - 鼠标按下时的位置)let offsetX = moveX - downX;//3.在这里判断一下:鼠标水平移动的距离 与 滑动成功的距离 之间的关系if (offsetX > distance) {offsetX = distance;//如果滑过了终点,就将它停留在终点位置} else if (offsetX < 0) {offsetX = 0;//如果滑到了起点的左侧,就将它重置为起点位置}//4.根据鼠标移动的距离来动态设置滑块的偏移量和背景颜色的宽度slider.style.left = offsetX + 'px';background.style.width = offsetX + 'px';//如果鼠标的水平移动距离 = 滑动成功的宽度if (offsetX == distance) {//1.设置滑动成功后的样式text.innerHTML = '验证成功';text.style.color = '#fff';slider.innerHTML = '<i class="el-icon-success"></i>';slider.style.color = '#53C300';//2.设置滑动成功后的状态success = this.activeValue;//成功后,清除掉鼠标按下事件和移动事件(因为移动时并不会涉及到鼠标松开事件)slider.onmousedown = null;document.onmousemove = null;//3.成功解锁后的回调函数setTimeout(() => {this.$emit('change', this.activeValue);}, 100);}};//四、给文档注册鼠标松开事件document.onmouseup = () => {//如果鼠标松开时,滑到了终点,则验证通过if (success == this.activeValue) return true;//反之,则将滑块复位(设置了1s的属性过渡效果)slider.style.left = 0;background.style.width = 0;slider.style.transition = 'left 1s ease';background.style.transition = 'width 1s ease';//只要鼠标松开了,说明此时不需要拖动滑块了,那么就清除鼠标移动和松开事件。document.onmousemove = null;document.onmouseup = null;};};/* 移动端 *///二、给滑块注册鼠标按下事件slider.ontouchstart = (event) => {const touch = event.changedTouches[0];//1.鼠标按下之前必须清除掉后面设置的过渡属性slider.style.transition = null;background.style.transition = null;//说明:clientX 事件属性会返回当事件被触发时,鼠标指针向对于浏览器页面(或客户区)的水平坐标。//2.当滑块位于初始位置时,得到鼠标按下时的水平位置const downX = touch.pageX;//三、给文档注册鼠标移动事件document.ontouchmove = (e) => {const tev = e.changedTouches[0];//1.获取鼠标移动后的水平位置const moveX = tev.pageX;//2.得到鼠标水平位置的偏移量(鼠标移动时的位置 - 鼠标按下时的位置)let offsetX = moveX - downX;//3.在这里判断一下:鼠标水平移动的距离 与 滑动成功的距离 之间的关系if (offsetX > distance) {offsetX = distance;//如果滑过了终点,就将它停留在终点位置} else if (offsetX < 0) {offsetX = 0;//如果滑到了起点的左侧,就将它重置为起点位置}//4.根据鼠标移动的距离来动态设置滑块的偏移量和背景颜色的宽度slider.style.left = offsetX + 'px';background.style.width = offsetX + 'px';//如果鼠标的水平移动距离 = 滑动成功的宽度if (offsetX == distance) {//1.设置滑动成功后的样式text.innerHTML = '验证成功';text.style.color = '#fff';slider.innerHTML = '√';slider.style.color = '#53C300';//2.设置滑动成功后的状态success = this.activeValue;//成功后,清除掉鼠标按下事件和移动事件(因为移动时并不会涉及到鼠标松开事件)slider.ontouchstart = null;document.ontouchmove = null;//3.成功解锁后的回调函数setTimeout(() => {this.$emit('change', this.activeValue);// console.log('解锁成功');}, 100);}};//四、给文档注册鼠标松开事件document.ontouchend = () => {//如果鼠标松开时,滑到了终点,则验证通过if (success == this.activeValue) return true;//反之,则将滑块复位(设置了1s的属性过渡效果)slider.style.left = 0;background.style.width = 0;slider.style.transition = 'left 1s ease';background.style.transition = 'width 1s ease';//只要鼠标松开了,说明此时不需要拖动滑块了,那么就清除鼠标移动和松开事件。document.ontouchmove = null;document.ontouchend = null;};};}}};</script><style scoped lang="scss">* {margin: 0px;padding: 0px;font-family: "微软雅黑";box-sizing: border-box;}.drag {height: 2.5rem;line-height: 2.5rem;background-color: #e8e8e8;position: relative;margin: 0 auto;border-radius: 3px;}.background {width: 2.5rem;height: 100%;position: absolute;background-color: #53C300;border-radius: 3px 0 0 3px;}.text {position: absolute;width: 100%;height: 100%;text-align: center;user-select: none;}.slider {width: 2.5rem;height: 2.375rem;position: absolute;border: 1px solid #ccc;cursor: move;font-family: "宋体";text-align: center;background-color: #fff;user-select: none;color: #666;}.shadow {text-align: center;background: -webkit-gradient(linear, left top, right top, color-stop(0, #4d4d4d), color-stop(.2, #5d5d5d),color-stop(.4, #6d6d6d), color-stop(.5, white), color-stop(.6, #6d6d6d), color-stop(.8, #5d5d5d), color-stop(1, #4d4d4d));-webkit-background-clip: text;-webkit-text-fill-color: transparent;-webkit-animation: animate 3s infinite;}@-webkit-keyframes animate {from {background-position: -80px;}to {background-position: 80px;}}@keyframes animate {from {background-position: -80px;}to {background-position: 80px;}}</style>
在登录页面集成登录滑块组件功能login.vue
<template><div class="container"><el-form ref="form" :model="form" :rules="rules" label-width="70px" class="login"><h3>ATP应用测试平台</h3><el-form-item label="用户名" prop="name"><el-input v-model="form.name"></el-input></el-form-item><el-form-item label="密码" prop="pass"><el-input v-model="form.pass" type="password" show-password></el-input></el-form-item><el-form-item label="验证" prop="isLock"><slider-verify-code v-model="form.isLock" @change="handlerLock"></slider-verify-code></el-form-item><el-button type="primary" @click="login" style="width: 100%;margin: 0;">立即登录</el-button></el-form></div></template><script>import sliderVerifyCode from '@/components/slider-verify-code.vue';export default {name: "Login",data() {const checkStatus = (rule, value, callback) => {if (!value) {return callback(new Error("请拖动滑块完成验证"));} else {if (this.form.name == '' || this.form.pass == ''|| !this.form.name || !this.form.pass) {setTimeout(() => {this.form.isLock = false;this.$refs.form.validateField('name');this.$refs.form.validateField('pass');return callback(new Error("验证未通过"));}, 1);}callback();}};return {form: {},rules: {name: [{required: true, message: '用户名称不得为空!', trigger: 'blur'},{min: 6, max: 18, message: '长度在 6 到 18 个字符', trigger: 'blur'}],pass: [{required: true, message: '密码不得为空!', trigger: 'blur'},{min: 6, max: 18, message: '长度在 6 到 18 个字符', trigger: 'blur'}],isLock: [{validator: checkStatus, trigger: 'blur'},],},}},components: {'slider-verify-code': sliderVerifyCode},methods: {//登录login() {this.$refs.form.validate((valid) => {if (valid) {this.$http.post('/sys/user/login', this.$qs.stringify(this.form)).then(res => {if (res.data.code === 1) {this.$router.push('/home')} else {this.$refs.form.resetFields();this.$message.warning(res.data.msg);}}).catch(error => {this.$message.error(error);});} else {return false;}});},handlerLock(data) {if (data) {this.$refs.form.validateField('isLock');}},}}</script><style scoped lang="scss">.container {width: 100%;height: 100%;overflow: auto;background: rgb(84, 92, 100);.login {text-align: center;padding: 20px 30px 30px 30px;margin: 10% auto;width: 320px;background: white;border-radius: 10px;h3 {margin: 30px 0px;}.el-form-item {margin-bottom: 35px;}}}</style>
验证
结语
ok,关于Vue基于ElementUI组件实现滑块登录验证组件的介绍就到这里了,我们下期见。。。