基于ElementUI 登录密码强度和滑动验证码组件

45 篇文章 1 订阅
21 篇文章 0 订阅

在使用登录验证 的时候,可以使用滑动验证码,本组件是基于ElementUI套件中的Progress 进度条加以改造的,使用正则表达式去实时验证密码的强度,讲真话,效果体验还是不错的。项目中只需要引入该组件集成到自己的项目中即可,方便模块化使用,本文基于  ElementUI admin 登录 疆这2个放在一起使用,下面我们来看看效果图:

 

 

 好了,以上废话不多说了,我们来看看实现过程,这个里面包含2个组件,一个密码强度组件和一个滑动组件,文件结构如下:

1.密码强度组件,在components文件夹下的 password-strength.vue文件中,代码如下 :

<template>
  <div id="container" style="margin-bottom: 16px">
    <el-row>
      <el-col :span="6" :offset="1">
        <el-progress :percentage="onePercentage" :color="oneCustomColors" :format="oneFormat" :stroke-width="3"></el-progress>
      </el-col>
      <el-col :span="6" :offset="1">
        <el-progress :percentage="twoPercentage" :color="twoCustomColors" :format="twoFormat" :stroke-width="3"></el-progress>
      </el-col>
      <el-col :span="6" :offset="1">
        <el-progress :percentage="ThreePercentage" :color="ThreeCustomColors" :format="ThreeFormat" :stroke-width="3"></el-progress>
      </el-col>
      <el-col :span="2" :offset="1" style="line-height: 15px;">
        {{content}}
      </el-col>
    </el-row>
  </div>
</template>

<script>
    export default {
        name: "PasswordStrength",
        model: {
            event: 'change',
            prop: 'passwordStr'
        },
        props: {
            //密码
            passwordStr: {
                type: [String,Boolean,Number,Object],
                required: true,
                default: "",
            },
        },
        watch:{
            passwordStr(newValue){
                const mode = this.checkPasswordStrength(newValue);
                //逻辑处理
                switch (mode) {
                    //初始化状态
                    case 0:
                        this.content = '';
                        this.onePercentage = 0;
                        this.twoPercentage = 0;
                        this.ThreePercentage = 0;
                        break;
                    case 1:
                        this.content = '弱';
                        this.onePercentage = 100;
                        this.twoPercentage = 0;
                        this.ThreePercentage = 0;
                        break;
                    case 2:
                        this.content = '中';
                        this.onePercentage = 100;
                        this.twoPercentage = 100;
                        this.ThreePercentage = 0;
                        break;
                    case 3:
                        this.content = '中';
                        this.onePercentage = 100;
                        this.twoPercentage = 100;
                        this.ThreePercentage = 0;
                        break;
                    default:
                        this.content = '高';
                        this.onePercentage = 100;
                        this.twoPercentage = 100;
                        this.ThreePercentage = 100;
                        break;
                }
            }
        },
        data(){
            return{
                content:"",
                onePercentage:0,
                twoPercentage:0,
                ThreePercentage:0,
                oneCustomColors: [
                    {color: '#f56c6c', percentage: 100}
                ],
                twoCustomColors: [
                    {color: '#e6a23c', percentage: 100}
                ],
                ThreeCustomColors: [
                    {color: '#67c23a', percentage: 100}
                ]
            }
        },
        methods:{
            oneFormat() {
                return "";
            },
            twoFormat() {
                return "";
            },
            ThreeFormat() {
                return "";
            },
            //密码强度验证
            checkPasswordStrength(value) {
                let mode = 0;
                //正则表达式验证符合要求的
                if (value.length < 1) return mode;
                if (/\d/.test(value)) mode++; //数字
                if (/[a-z]/.test(value)) mode++; //小写
                if (/[A-Z]/.test(value)) mode++; //大写
                if (/\W/.test(value)) mode++; //特殊字符
                return mode;
            }
        }
    }
</script>

<style>
  .el-progress__text {
    display: none;
  }
  .el-progress-bar {
    padding-right: 0px;
    margin: 0px;
  }
</style>

2.滑动组件,在components文件夹下的 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 = 0
        return 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 = '#3fcd26';
                //二、给滑块注册鼠标按下事件
                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 = '#53C300';
                            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 = '#53C300';
                            slider.innerHTML = '&radic;';
                            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>

3.模板文件index.vue,在模板文件中引入2面2个组件,代码如下 :

<template>
<div class="login">
  <div class="login-con">
    <div class="ivu-card">
      <div class="ivu-card-head"><p><i class="ivu-icon ivu-icon-log-in"></i> <span>欢迎登录-环球芯力系统</span></p>
      </div>
      <div class="ivu-card-body">
        <div class="form-con">
          <el-form ref="loginForm" :model="loginForm" :rules="rules" label-width="0px" class="login-form" @keydown.enter.native="handleAccountSubmit">
            <el-form-item prop="username" class="login-form-txt">
              <span class="svg-container">
                <svg-icon icon-class="user" />
              </span>
              <el-input refix-icon="user" v-model="loginForm.username" placeholder="请输入用户名"></el-input>
            </el-form-item>
            <el-form-item prop="password" class="login-form-txt">
              <span class="svg-container">
                <svg-icon icon-class="password" />
              </span>
              <el-input  v-model="loginForm.password" type="password" placeholder="请输入密码"></el-input>
            </el-form-item>

            <div class="progress-bar_wrap">
              <password-strength v-model="loginForm.password" style="padding-top: 10px;"></password-strength>
            </div>

            <el-form-item prop="isLock">
              <slider-verify-code v-model="loginForm.isLock" @change="handlerLock"></slider-verify-code>
            </el-form-item>

            <el-button :loading="loading" type="primary" @click="login" style="width: 100%;margin: 0;">登录</el-button>
          </el-form>
          <p class="login-tip">Copyright © 2000 - 2021 环球芯力系统有限公司 All Rights Reserved</p>
          <p class="login-icp"><a href="http://www.beian.miit.gov.cn/" target="_blank">沪ICP备888999号</a></p>
        </div>
      </div>
    </div>
  </div>
</div>
</template>

<script>
    import  './login.less'
    import PasswordStrength  from '../login/components/password-strength.vue';
    import sliderVerifyCode from '../login/components/slider-verify-code.vue';

    export default {
        name: "Login",
        data() {
            const checkStatus = (rule, value, callback) => {
                if (!value) {
                    return callback(new Error("请拖动滑块完成验证"));
                } else {
                    if (this.loginForm.username == '' || this.loginForm.password == ''
                        || !this.loginForm.username || !this.loginForm.password) {
                        setTimeout(() => {
                            this.loginForm.isLock = false;
                            this.$refs.loginForm.validateField('username');
                            this.$refs.loginForm.validateField('password');
                            return callback(new Error("验证未通过"));
                        }, 1);
                    }
                    callback();
                }
            };
            return {
                loginForm: {
                    username: 'admin',
                    password: '111111',
                    passwordStr:'',
                },
                rules: {
                    username: [
                        {required: true, message: '用户名称不得为空!', trigger: 'blur'},
                        {min: 5, max: 18, message: '长度在 5 到 18 个字符', trigger: 'blur'}
                    ],
                    password: [
                        {required: true, message: '密码不得为空!', trigger: 'blur'},
                        {min: 5, max: 18, message: '长度在 5 到 18 个字符', trigger: 'blur'}
                    ],
                    isLock: [
                        {validator: checkStatus, trigger: 'blur'},
                    ],
                },
                redirect: undefined,
                otherQuery: {},
                loading: false,
            }
        },
        components: {
            'slider-verify-code': sliderVerifyCode,
            'password-strength': PasswordStrength,
        },
        watch: {
            $route: {
                handler: function(route) {
                    const query = route.query
                    if (query) {
                        this.redirect = query.redirect
                        this.otherQuery = this.getOtherQuery(query)
                    }
                },
                immediate: true
            }
        },
        methods: {
            //登录
            login() {
                this.$refs.loginForm.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.loginForm.resetFields();
                                 this.$message.warning(res.data.msg);
                             }
                         }).catch(error => {
                             this.$message.error(error);
                        });                         
                    } else {
                        return false;
                    }
                });
            },

            handlerLock(data) {
                if (data) {
                    this.$refs.loginForm.validateField('isLock');
                }
            },
        }
    }
</script>

<style>
  .ivu-card {
    display: block;
    background: #fff;
    border-radius: 4px;
    font-size: 14px;
    position: relative;
    -webkit-transition: all .2s ease-in-out;
    transition: all .2s ease-in-out;
  }
  .ivu-card-head {
    border-bottom: 1px solid #e8eaec;
    padding: 14px 16px;
    line-height: 1;
  }
  .ivu-card-body {
    padding: 16px;
  }

  .login-form-txt>>>.el-input__inner{
    border: 1px solid #d9d3d3!important;
    border-radius: 5px!important;
    color: #454545!important;
  }

</style>


<style lang="scss">

  $bg:#283443;
  $light_gray:#fff;
  $cursor: #515a6e;

  @supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
    .login-form .el-input input {
      color: $cursor;
    }
  }

  /* reset element-ui css */
  .login-form {
    .el-input {
      display: inline-block;
      height: 47px;
      width: 85%;

      input {
        background: transparent;
        border: 0px;
        -webkit-appearance: none;
        border-radius: 0px;
        padding: 12px 5px 12px 15px;
        color: #272424;
        height: 47px;
        caret-color: $cursor;

        &:-webkit-autofill {
          box-shadow: 0 0 0px 1000px $bg inset !important;
          -webkit-text-fill-color: $cursor !important;
        }

        .el-input__inner:focus{
          border: 1px solid #d9d3d3;
          border-radius: 5px;
          color:  #57a3f3;
        }

        &:focus {
          border-color: hsl(199, 98%, 48%);
        }
      }

    }

    .el-form-item {
      border: 1px solid #d9d3d3;
      border-radius: 5px;
      color: #454545;
    }

    .login-form-txt:hover{
      border: 1px solid #57a3f3;
      border-radius: 5px;
      color: #57a3f3;
    }
  }
</style>

<style lang="scss" scoped>
  $bg:#2d3a4b;
  $dark_gray:#889aa4;
  $light_gray:#eee;

  .login-form {
    min-height: 100%;
    width: 100%;
    overflow: hidden;

    .login-form {
      position: relative;
      width: 520px;
      max-width: 100%;
      padding: 160px 35px 0;
      margin: 0 auto;
      overflow: hidden;

    }

    .tips {
      font-size: 14px;
      color: #fff;
      margin-bottom: 10px;

      span {
        &:first-of-type {
          margin-right: 16px;
        }
      }
    }

    .svg-container {
      padding: 6px 5px 6px 15px;
      vertical-align: middle;
      width: 30px;
      display: inline-block;
    }

    .title-container {
      position: relative;

      .title {
        font-size: 26px;
        margin: 0px auto 40px auto;
        text-align: center;
        font-weight: bold;
      }

      .set-language {
        color: #fff;
        position: absolute;
        top: 3px;
        font-size: 18px;
        right: 0px;
        cursor: pointer;
      }
    }

    .show-pwd {
      position: absolute;
      right: 10px;
      top: 7px;
      font-size: 16px;
      cursor: pointer;
      user-select: none;
    }

    .thirdparty-button {
      position: absolute;
      right: 0;
      bottom: 6px;
    }

    @media only screen and (max-width: 470px) {
      .thirdparty-button {
        display: none;
      }
    }
  }
</style>


<style lang="scss" scoped>
  @mixin jc-flex{
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .jc-component__range{
    .jc-range{
      background-color: #e9e9e9;
      position: relative;
      transition: 1s all;
      user-select: none;
      color: #585858;
      @include jc-flex;
      height: 50px; /*no*/
      &.success{
        background-color: #3bc923;
        color: #fff;
        i{
          color: #3bc923;
        }
      }
      i{
        position: absolute;
        left: 0;
        width: 50px;/*no*/
        height: 100%;
        color: #3fcd26;
        background-color: #fff;
        border: 1px solid #d8d8d8;
        cursor: pointer;
        font-size: 24px;
        @include jc-flex;
      }
    }
  }
</style>

<style scope>
  .el-icon-check:before {
    content: "\e6da";
  }
</style>

4.login的样式框css

.login{
    width: 100%;
    height: 100%;
    background-image: url('~@/assets/images/login-bg.jpg');
    background-size: cover;
    background-position: center;
    position: relative;
    &-con{
        position: absolute;
        right: 160px;
        top: 50%;
        transform: translateY(-60%);
        width: 420px;
        &-header{
            font-size: 16px;
            font-weight: 300;
            text-align: center;
            padding: 30px 0;
        }
        .form-con{
            padding: 10px 0 0;
        }
        .login-tip{
            font-size: 10px;
            text-align: center;
            color: #c3c3c3;
        }
        .login-icp{
			padding: 10px 0 0;
            font-size: 10px;
            text-align: center;
        }
        .login-icp a{
            color: #2d8cf0;
        }
        .login-reg{
            text-align: center;
            margin-bottom: 10px;
        }
    }
}

以上就是element ui admin 登录密码框轻度和滑动验证的一个例子,当然了,这种滑动验证是放在前段的 安全性也没有保障,一般都是放在后端动态生成,本案例可以学习一下这种组件使用

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
你可以使用 React Swipeable 库来实现基于 React 的滑动图片验证码组件。该库提供了一个 Swipeable 组件,可以帮助你快速实现滑动操作。 下面是一个基本的滑动图片验证码组件的示例: ```jsx import React, { useState } from 'react'; import Swipeable from 'react-swipeable'; const SlideCaptcha = ({ onSuccess, onFailure }) => { const [isSliding, setIsSliding] = useState(false); const [sliderPosition, setSliderPosition] = useState(0); const [sliderWidth, setSliderWidth] = useState(0); const handleSwipeStart = () => { setIsSliding(true); }; const handleSwipeMove = (event, deltaX) => { if (!isSliding) return; const newSliderPosition = Math.max(0, Math.min(sliderWidth, sliderPosition + deltaX)); setSliderPosition(newSliderPosition); }; const handleSwipeEnd = () => { setIsSliding(false); if (sliderPosition >= sliderWidth * 0.9) { onSuccess(); } else { onFailure(); setSliderPosition(0); } }; const handleSliderRef = (node) => { if (node) { setSliderWidth(node.offsetWidth); } }; return ( <div> <div className="captcha-image" /> <Swipeable className="slider" onSwiping={handleSwipeMove} onSwipingStart={handleSwipeStart} onSwipingEnd={handleSwipeEnd} > <div className="slider-inner" ref={handleSliderRef}> <div className="slider-handle" style={{ left: sliderPosition }} /> </div> </Swipeable> </div> ); }; export default SlideCaptcha; ``` 在这个示例中,我们使用 Swipeable 组件来监听滑动事件,并根据滑动距离来移动滑块的位置。当滑块移动到指定位置时,触发 onSuccess 回调函数。如果滑块没有移动到指定位置,将调用 onFailure 回调函数并将滑块重置。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值