• .NET 6 实现滑动验证码(十)、大结局:前端代码实战,vue3与HTML+JQuery


    上一篇文章介绍了搭建验证码服务端API,这篇文章介绍前端代码的搭建,在实际应用中,我有写过两种类型的前端,一个是vue3的,一个是纯HTML+JQuery的。至于vue2,大家根据vue3稍作改动即可。

    Vue3前端代码实现

    vue3中,把滑动验证码写成了一个组件,在业务场景,直接调用组件即可,在一般使用过程中,只有在发送短信验证码的时候,我才调用滑动验证码来进行行为认证。

    HTML代码

    <template>
      <div
        class="captcha"
        style="margin-right: auto; margin-left: auto"
        :style="captchaWrapperStyle"
      >
        <div class="captcha__main" :style="imgWrapperStyle">
          <img
            v-if="src"
            ref="backgroundRef"
            alt="background"
            class="captcha_background"
            :src="src"
          />
          <img
            v-show="sliderSrc"
            ref="sliderRef"
            alt="slider"
            class="captcha_slider"
            :class="{ goFirst: isOk, goKeep: isKeep }"
            :src="sliderSrc"
          />
          <div v-if="showVerifyTip" class="captcha_message">
            <div class="captcha_message__icon">
              <svg
                v-if="isPassing"
                height="28"
                viewBox="0 0 28 28"
                width="28"
                xmlns="http://www.w3.org/2000/svg"
              >
                <g
                  fill="none"
                  fill-rule="evenodd"
                  stroke="#fff"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                  stroke-width="1.5"
                >
                  <path
                    d="M22.776 4.073A13.2 13.2 0 0 0 14 .75C6.682.75.75 6.682.75 14S6.682 27.25 14 27.25 27.25 21.318 27.25 14c0-.284-.009-.566-.027-.845"
                  />
                  <path d="M7 12.5l7 7 13-13" />
                g>
              svg>
              <svg
                v-else
                height="28"
                viewBox="0 0 28 28"
                width="28"
                xmlns="http://www.w3.org/2000/svg"
              >
                <g fill="none" fill-rule="evenodd" stroke="#fff" stroke-width="1.5">
                  <circle cx="14" cy="14" r="13.25" />
                  <path
                    d="M8.75 8.75l10.5 10.5M19.25 8.75l-10.5 10.5"
                    stroke-linecap="round"
                    stroke-linejoin="round"
                  />
                g>
              svg>
            div>
            <div class="captcha_message__text">
              {{ isPassing ? successTip : failTip }}
            div>
          div>
          <div v-if="showGenerateLoadding" class="captcha_message loadding">
            <div
              class="captcha_message__icon captcha_message__icon--loadding"
            >div>
            <div class="captcha_message__text">加载中...div>
          div>
          <div v-if="showVerifyLoadding" class="captcha_message">
            <div
              class="captcha_message__icon captcha_message__icon--loadding"
            >div>
            <div class="captcha_message__text">div>
          div>
        div>
        <div ref="dragVerifyRef" class="captcha__bar" :style="dragVerifyStyle">
          <div
            ref="progressBarRef"
            class="captcha_progress_bar"
            :class="{ goFirst2: isOk }"
            :style="progressBarStyle"
          >div>
          <div class="captcha_progress_bar__text" :style="textStyle">
            {{ text }}
          div>
          <div
            ref="handlerRef"
            class="captcha_handler"
            :class="{ goFirst: isOk }"
            :style="handlerStyle"
            @mousedown="handleDragStart"
            @touchstart="handleDragStart"
          >
            <svg
              p-id="819"
              :style="handlerSvgStyle"
              version="1.1"
              viewBox="0 0 1024 1024"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                d="M500.864 545.728a47.744 47.744 0 0 0 6.72-48.896 24.704 24.704 0 0 0-4.48-8.384L240.256 193.088a34.24 34.24 0 0 0-28.608-17.408 34.24 34.24 0 0 0-25.856 12.864 46.592 46.592 0 0 0 0 59.52l238.08 264.512-238.08 264.512a46.592 46.592 0 0 0-1.088 59.52 32 32 0 0 0 50.56 0l265.6-290.88z"
                p-id="820"
              />
              <path
                d="M523.84 248.064l236.992 264.512-238.08 264.512a46.592 46.592 0 0 0 0 59.52 32 32 0 0 0 50.56 0l265.6-292.608a47.744 47.744 0 0 0 6.72-48.832 24.704 24.704 0 0 0-4.48-8.448L578.304 191.36a34.24 34.24 0 0 0-55.552-2.816 46.592 46.592 0 0 0 1.088 59.52z"
                p-id="821"
              />
            svg>
          div>
        div>
        <div v-if="showRefresh" class="captcha__actions">
          <a
            class="captcha__action"
            :style="refreshTextColorStyle"
            @click="handleRefresh"
          >
            <svg
              :fill="refreshColorStyle"
              height="20px"
              version="1.1"
              viewBox="0 0 20 20"
              width="20px"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                d="M10,4 C12.0559549,4 13.9131832,5.04358655 15.0015086,6.68322231 L15,5.5 C15,5.22385763 15.2238576,5 15.5,5 C15.7761424,5 16,5.22385763 16,5.5 L16,8.5 C16,8.77614237 15.7761424,9 15.5,9 L12.5,9 C12.2238576,9 12,8.77614237 12,8.5 C12,8.22385763 12.2238576,8 12.5,8 L14.5842317,8.00000341 C13.7999308,6.20218044 12.0143541,5 10,5 C7.23857625,5 5,7.23857625 5,10 C5,12.7614237 7.23857625,15 10,15 C11.749756,15 13.3431487,14.0944653 14.2500463,12.6352662 C14.3958113,12.4007302 14.7041063,12.328767 14.9386423,12.4745321 C15.1731784,12.6202971 15.2451415,12.9285921 15.0993765,13.1631281 C14.0118542,14.9129524 12.0990688,16 10,16 C6.6862915,16 4,13.3137085 4,10 C4,6.6862915 6.6862915,4 10,4 Z"
                fill-rule="nonzero"
              />
            svg>
            
          a>
        div>
      div>
    template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139

    html代码中,一些必要的图片使用svg的形式表示,代码中的图片包括有正确图标、错误图标、滑块图片、刷新图片。在此基础上,还可以扩展自己需要的图片。

    CSS代码

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196

    JS代码

    <script>
      export default defineComponent({
        name: 'SlideCaptcha',
        props: {
          width: {
            type: Number,
            default: 340,
          },
          height: {
            type: Number,
            default: 212,
          },
          barHeight: {
            type: Number,
            default: 40,
          },
          handlerIconWidth: {
            type: Number,
            default: 16,
          },
          handlerIconHeigth: {
            type: Number,
            default: 16,
          },
          background: {
            type: String,
            default: '#eee',
          },
          circle: {
            type: Boolean,
            default: false,
          },
          radius: {
            type: String,
            default: '4px',
          },
          text: {
            type: String,
            default: '按住滑块拖动',
          },
          progressBarBg: {
            type: String,
            default: '#76c61d',
          },
          successTip: {
            type: String,
            default: '验证通过,超过80%用户',
          },
          failTip: {
            type: String,
            default: '验证未通过,拖动滑块将悬浮图像正确合并',
          },
          showRefresh: {
            type: Boolean,
            default: false,
          },
          refreshColor: {
            type: String,
            default: '#505050',
          },
        },
        emits: ['finish', 'refresh'],
        setup(props, context) {
          const state = reactive({
            isMoving: false,
            x: 0,
            y: 0,
            isOk: false,
            isKeep: false,
            isFinish: false,
            tracks: [],
            startSlidingTime: undefined,
            showVerifyTip: false,
            showVerifyLoadding: false,
            showGenerateLoadding: false,
            src: '',
            sliderSrc: '',
            isPassing: false,
          })
          const imgWrapperStyle = computed(() => {
            return {
              width: props.width + 'px',
              height: props.height + 'px',
              position: 'relative',
              overflow: 'hidden',
            }
          })
          const captchaWrapperStyle = computed(() => {
            return {
              width: props.width + 'px',
            }
          })
          const dragVerifyStyle = computed(() => {
            return {
              width: props.width + 'px',
              height: props.barHeight + 'px',
              lineHeight: props.barHeight + 'px',
              background: props.background,
              borderRadius: props.circle
                ? props.barHeight / 2 + 'px'
                : props.radius,
            }
          })
          const progressBarStyle = computed(() => {
            return {
              background: props.progressBarBg,
              height: props.barHeight + 'px',
              borderRadius: props.circle
                ? props.barHeight / 2 + 'px 0 0 ' + props.barHeight / 2 + 'px'
                : props.radius,
            }
          })
          const textStyle = computed(() => {
            return {
              height: props.barHeight + 'px',
              width: props.width + 'px',
              //fontSize: this.textSize,
            }
          })
          const handlerStyle = computed(() => {
            return {
              width: props.barHeight + 'px',
              height: props.barHeight - 2 + 'px',
              //background: this.handlerBg,
            }
          })
          const handlerSvgStyle = computed(() => {
            return {
              width: props.handlerIconWidth + 'px',
              height: props.handlerIconHeigth + 'px',
            }
          })
          const refreshColorStyle = computed(() => {
            return props.refreshColor
          })
          const refreshTextColorStyle = computed(() => {
            return {
              color: props.refreshColor,
            }
          })
    
          const dragVerifyRef = ref(null)
          const progressBarRef = ref(null)
          const backgroundRef = ref(null)
          const sliderRef = ref(null)
          const handlerRef = ref(null)
          onMounted(() => {
            const dragEl = dragVerifyRef
            dragEl.value.style.setProperty('--textColor', '#333')
            dragEl.value.style.setProperty(
              '--width',
              Math.floor(props.width / 2) + 'px'
            )
            dragEl.value.style.setProperty(
              '--pwidth',
              -Math.floor(props.width / 2) + 'px'
            )
          })
          // 开始请求生成图片时调用
          const startRequestGenerate = () => {
            reset()
            state.showGenerateLoadding = true
          }
          // 结束请求生成图片时调用
          const endRequestGenerate = (src, sliderSrc) => {
            state.showGenerateLoadding = false
            state.src = src
            state.sliderSrc = sliderSrc
          }
          // 开始请求校验时调用
          const startRequestVerify = () => {
            state.showVerifyLoadding = true
          }
          // 结束请求校验时调用
          const endRequestVerify = (isPassing) => {
            state.isPassing = isPassing
            state.showVerifyLoadding = false
            state.showVerifyTip = true
          }
          const reset = () => {
            state.x = 0
            state.y = 0
            state.tracks = []
            state.isMoving = false
            state.isFinish = false
            state.showGenerateLoadding = false
            state.showVerifyLoadding = false
            state.showVerifyTip = false
            state.isPassing = false
            if (progressBarRef) progressBarRef.value.style.width = 0
            if (sliderRef) sliderRef.value.style.left = 0
            if (handlerRef) handlerRef.value.style.left = 0
          }
          const removeEventListeners = () => {
            window.removeEventListener('touchmove', handleDragMoving)
            window.removeEventListener('touchend', handleDragFinish)
            window.removeEventListener('mousemove', handleDragMoving)
            window.removeEventListener('mouseup', handleDragFinish)
          }
          const handleDragStart = (e) => {
            if (
              !state.isPassing &&
              state.src &&
              state.sliderSrc &&
              !state.isFinish
            ) {
              window.addEventListener('touchmove', handleDragMoving)
              window.addEventListener('touchend', handleDragFinish)
              window.addEventListener('mousemove', handleDragMoving)
              window.addEventListener('mouseup', handleDragFinish)
    
              state.isMoving = true
              state.startSlidingTime = new Date()
              state.x = e.pageX || e.touches[0].pageX
              state.y = e.pageY || e.touches[0].pageY
            }
          }
          const handleDragMoving = (e) => {
            if (
              state.isMoving &&
              !state.isPassing &&
              state.src &&
              state.sliderSrc &&
              !state.isFinish
            ) {
              const _x = (e.pageX || e.touches[0].pageX) - state.x
              const _y = (e.pageY || e.touches[0].pageY) - state.y
    
              handlerRef.value.style.left = _x + 'px'
              progressBarRef.value.style.width = _x + props.barHeight / 2 + 'px'
              sliderRef.value.style.left = _x + 'px'
    
              state.tracks.push({
                x: Math.round(_x),
                y: Math.round(_y),
                t: new Date().getTime() - state.startSlidingTime.getTime(),
              })
            }
          }
          const handleDragFinish = () => {
            if (
              state.isMoving &&
              !state.isPassing &&
              state.src &&
              state.sliderSrc &&
              !state.isFinish
            ) {
              state.isMoving = false
              state.isFinish = true
              removeEventListeners()
              context.emit('finish', {
                backgroundImageWidth: backgroundRef.value.offsetWidth,
                backgroundImageHeight: backgroundRef.value.offsetHeight,
                sliderImageWidth: sliderRef.value.offsetWidth,
                sliderImageHeight: sliderRef.value.offsetHeight,
                startTime: state.startSlidingTime,
                endTime: new Date(),
                tracks: state.tracks,
              })
            }
          }
          const handleRefresh = () => {
            reset()
            context.emit('refresh')
          }
    
          onUnmounted(() => {
            removeEventListeners()
          })
          onDeactivated(() => {
            removeEventListeners()
          })
          return {
            ...toRefs(state),
            imgWrapperStyle,
            captchaWrapperStyle,
            dragVerifyStyle,
            progressBarStyle,
            textStyle,
            handlerStyle,
            handlerSvgStyle,
            refreshColorStyle,
            refreshTextColorStyle,
            dragVerifyRef,
            progressBarRef,
            backgroundRef,
            sliderRef,
            handlerRef,
            startRequestGenerate,
            endRequestGenerate,
            startRequestVerify,
            endRequestVerify,
            reset,
            removeEventListeners,
            handleDragStart,
            handleDragMoving,
            handleDragFinish,
            handleRefresh,
          }
        },
      })
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302

    代码定义了两个emit,一个是刷新,一个是拖动结束。代码还是比较简单。

    组件的调用

    <template>
      <div class="login-container">
        <div class="captcha_box">
          <slide-captcha
            ref="captchaRef"
            :fail-tip="failTip"
            :height="height"
            refresh-color="#FFFFFF"
            :show-refresh="true"
            :success-tip="successTip"
            :width="width"
            @finish="handleFinish"
             @refresh="generate"
          />
        div>
      div>
    template>
    
    <script>
     
      import SlideCaptcha from './components/SlideCaptcha.vue'
      import { checkCaptcha, captcha } from '@/api/user'
    
      export default defineComponent({
        name: 'Captcha',
        components: { SlideCaptcha },
        setup() {
          const captchaRef = ref(null)
          const state = reactive({
            width: 340,
            height: 212,
            failTip: '',
            successTip: '',
            requestId: undefined,
          })
    
          
          const captchaShow = async () => {
            await generate()
          }
          //生成验证码
          const generate = async () => {
            nextTick(async () => {
              captchaRef.value.startRequestGenerate()
              //封装了一个方法,其实就是使用axios发送了一个get请求。
              await captcha().then((result) => {
                if (result.success) {
                  state.requestId = result.data.Id
                  captchaRef.value.endRequestGenerate(
                    result.data.BackgroundImage,
                    result.data.SliderImage
                  )
                } else {
                  captchaRef.value.endRequestGenerate(null, null)
                }
              })
            })
          }
          //验证码校验
          const handleFinish = async (data) => {
            nextTick(async () => {
              captchaRef.value.startRequestVerify()
              //封装了一个方法,其实就是使用axios发送了一个get请求。
              await checkCaptcha(state.requestId, data).then(async (result) => {
                if (result.success) {
                  state.successTip = '验证通过'
                  captchaRef.value.endRequestVerify(result.success)
                  
                } else {
                  state.failTip = '验证未通过'
                  await generate()
                }
              })
            })
          }
          return {
            ...toRefs(state),
            captchaShow,
            handleFinish,
            generate,
            captchaRef,
          }
        },
      })
    script>
    
    <style lang="scss" scoped>
      
      .captcha_box {
        position: relative;
        max-width: 100%;
        padding: 4.5vh;
        margin: calc((100vh - 555px) / 2) 5vw 5vw;
        overflow: hidden;
        
      }
    style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98

    HTML+JQuery实现

    一般在管理系统中,使用vue来实现又快又好,但在实际使用过程中,甲方爸爸要求不能用vue来写前端,因为要考虑到搜索引擎优化(哎,此处省略好多字…)。这时就需要传统html代码来实现了。

    HTML代码

    <div class="captcha" style="width:365px;margin:0 auto;margin-top:20vh;">
        <div class="captcha__main" style="position:relative; overflow:hidden;width:365px; height:228px;">
            <img src="" class="captcha_background" id="captchaSrc" />
            <img src="" class="captcha_slider" id="sliderSrc" />
            <div class="captcha_message" id="showVerifyTip">
                <div class="captcha_message__icon">
                    <svg style="display:none;" id="isPassing" height="28" width="28" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg">
                        <g fill="none" fill-rule="evenodd" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5">
                            <path d="M22.776 4.073A13.2 13.2 0 0 0 14 .75C6.682.75.75 6.682.75 14S6.682 27.25 14 27.25 27.25 21.318 27.25 14c0-.284-.009-.566-.027-.845" />
                            <path d="M7 12.5l7 7 13-13" />
                        g>
                    svg>
                    <svg style="display:none;" id="isFail" height="28" width="28" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg">
                        <g fill="none" fill-rule="evenodd" stroke="#fff" stroke-width="1.5">
                            <circle cx="14" cy="14" r="13.25" />
                            <path d="M8.75 8.75l10.5 10.5M19.25 8.75l-10.5 10.5" stroke-linecap="round" stroke-linejoin="round" />
                        g>
                    svg>
                div>
                <div class="captcha_message__text" id="resultTipMessage">div>
            div>
            <div class="captcha_message loadding" id="showGenerateLoadding">
                <div class="captcha_message__icon captcha_message__icon--loadding">div>
                <div class="captcha_message__text">加载中...div>
            div>
            <div class="captcha_message" id="showVerifyLoadding">
                <div class="captcha_message__icon captcha_message__icon--loadding">div>
                <div class="captcha_message__text">请稍等...div>
            div>
        div>
        <div class="captcha__bar" style="width:365px;height:40px; line-height:40px;background:#eee;border-radius:4px;">
            <div class="captcha_progress_bar" style="background:#76c61d;height:40px;border-radius:4px;">div>
            <div class="captcha_progress_bar__text" style="width:365px;height:40px;">按住滑块拖动div>
            <div class="captcha_handler" style="width:40px;height:38px;" onmousedown="captcha.handleDragStart(event)" ontouchstart="captcha.handleDragStart(event)">
                <svg p-id="819" style="width:16px;height:16px;" version="1.1" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
                    <path d="M500.864 545.728a47.744 47.744 0 0 0 6.72-48.896 24.704 24.704 0 0 0-4.48-8.384L240.256 193.088a34.24 34.24 0 0 0-28.608-17.408 34.24 34.24 0 0 0-25.856 12.864 46.592 46.592 0 0 0 0 59.52l238.08 264.512-238.08 264.512a46.592 46.592 0 0 0-1.088 59.52 32 32 0 0 0 50.56 0l265.6-290.88z"
                          p-id="820" />
                    <path d="M523.84 248.064l236.992 264.512-238.08 264.512a46.592 46.592 0 0 0 0 59.52 32 32 0 0 0 50.56 0l265.6-292.608a47.744 47.744 0 0 0 6.72-48.832 24.704 24.704 0 0 0-4.48-8.448L578.304 191.36a34.24 34.24 0 0 0-55.552-2.816 46.592 46.592 0 0 0 1.088 59.52z"
                          p-id="821" />
                svg>
            div>
        div>
        <div class="captcha__actions">
            <a class="captcha__action closeCaptcha" style="color:#505050;">
                <svg t="1663301405680" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2376" width="15" height="15"><path d="M511.232 438.8352L112.9984 40.6016A51.2 51.2 0 0 0 40.6016 112.9984L438.784 511.232 40.6016 909.4656a51.2 51.2 0 1 0 72.3968 72.448l398.2336-398.2848 398.2336 398.2848a51.2 51.2 0 1 0 72.448-72.448l-398.2848-398.2336 398.2848-398.2336A51.2 51.2 0 0 0 909.4656 40.6016L511.232 438.784z" p-id="2377" fill="#505050">path>svg>
            a>
            <a class="captcha__action refreshCaptcha" style="color:#505050;">
                <svg style="color:#505050;"
                     height="25px"
                     version="1.1"
                     viewBox="0 0 20 20"
                     width="25px"
                     xmlns="http://www.w3.org/2000/svg">
                    <path d="M10,4 C12.0559549,4 13.9131832,5.04358655 15.0015086,6.68322231 L15,5.5 C15,5.22385763 15.2238576,5 15.5,5 C15.7761424,5 16,5.22385763 16,5.5 L16,8.5 C16,8.77614237 15.7761424,9 15.5,9 L12.5,9 C12.2238576,9 12,8.77614237 12,8.5 C12,8.22385763 12.2238576,8 12.5,8 L14.5842317,8.00000341 C13.7999308,6.20218044 12.0143541,5 10,5 C7.23857625,5 5,7.23857625 5,10 C5,12.7614237 7.23857625,15 10,15 C11.749756,15 13.3431487,14.0944653 14.2500463,12.6352662 C14.3958113,12.4007302 14.7041063,12.328767 14.9386423,12.4745321 C15.1731784,12.6202971 15.2451415,12.9285921 15.0993765,13.1631281 C14.0118542,14.9129524 12.0990688,16 10,16 C6.6862915,16 4,13.3137085 4,10 C4,6.6862915 6.6862915,4 10,4 Z"
                          fill-rule="nonzero" />
                svg>
            a>
        div>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    没有了vue的双向绑定,需要定义一堆id来确保dom操作。确实比较麻烦。

    CSS代码

    其实把vue的css代码拷贝了一下。

    .captcha {user-select:none;background:#ffffff;padding:5px;border-radius:3px;}
    .captcha__main {background:rgb(244,245,246);}
    .captcha_background {width:100%;}
    .captcha_slider {position:absolute;top:0;left:0;display:block;height:100%;}
    .captcha_message_box {width:100%;height:100%;position:absolute;top:0px;left:0px;z-index:999999}
    .captcha_message {position:absolute;top:0px;left:0px;z-index:999998;display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;height:100%;background-color:rgba(34,34,34,0.85);-webkit-box-pack:center;-webkit-box-align:center;}
    .captcha_message__icon {width:28px;height:28px;margin:0px auto;}
    .captcha_message__icon--loadding {width:24px;height:24px;background-image:url(data:image/png;base64,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);background-repeat:no-repeat;background-position:center center;background-size:contain;border-radius:50%;animation:1s linear 0s infinite normal none running turn;}
    .captcha_message.loadding {background-color:rgb(244 245 246);}
    .captcha_message__text {display:inline-block;max-width:200px;padding:10px;font-size:14px;color:rgb(255,255,255);text-align:center;}
    .captcha_message.loadding .captcha_message__text {color:rgb(202,202,202);}
    .captcha__bar {position:relative;width:100%;margin-top:5px;overflow:hidden;text-align:center;}
    .captcha_progress_bar {position:absolute;width:0;}
    .captcha_progress_bar__text {position:absolute;top:0px;width:100%;font-size:12px;color:transparent;-moz-user-select:none;-webkit-user-select:none;-o-user-select:none;-ms-user-select:none;user-select:none;background:-webkit-gradient( linear,left top,right top,color-stop(0,var(--textColor)),color-stop(0.4,var(--textColor)),color-stop(0.5,#fff),color-stop(0.6,var(--textColor)),color-stop(1,var(--textColor)) );-webkit-background-clip:text;animation:slidetounlock 3s infinite;-webkit-animation:slidetounlock 3s infinite;-webkit-text-fill-color:transparent;-webkit-text-size-adjust:none;}
    .captcha_handler {position:absolute;top:0px;left:0px;display:flex;align-items:center;justify-content:center;margin:1px;cursor:move;background:rgb(255,255,255);}
    .captcha__actions {display:flex;align-items:center;justify-content:flex-start;min-height:20px;padding:16px 20px 20px 0px;line-height:20px;color:rgb(80,80,80);-webkit-box-pack:justify;-webkit-box-align:center;}
    .captcha__action__text {font-size:14px !important;color:rgb(80,80,80);}
    .captcha__action {display:flex;align-items:center;text-decoration:none;cursor:pointer;margin-left:5px;margin-right:5px;}
    .goFirst {left:0px !important;transition:left 0.5s;}
    .goKeep {transition:left 0.2s;}
    .goFirst2 {width:0px !important;transition:width 0.5s;}
    @keyframes slidetounlock {0% {background-position:var(--pwidth) 0;}
    100% {background-position:var(--width) 0;}
    }
    @-webkit-keyframes slidetounlock {0% {background-position:var(--pwidth) 0;}
    100% {background-position:var(--width) 0;}
    }
    @keyframes slidetounlock2 {0% {background-position:var(--pwidth) 0;}
    100% {background-position:var(--pwidth) 0;}
    }
    @keyframes turn {0% {-webkit-transform:rotate(0deg);}
    25% {-webkit-transform:rotate(90deg);}
    50% {-webkit-transform:rotate(180deg);}
    75% {-webkit-transform:rotate(270deg);}
    100% {-webkit-transform:rotate(360deg);}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    Javascript代码(Jquery)

    因为最开始学习前端的时候,接触的最多的就是Jquery。所以就使用Jquery来吧。

    <script type="text/javascript">
        //定义captcha
        const captcha = {
            x: 0,
            y: 0,
            tracks: [],
            isPassing: false,
            isFinish: false,
            isMoving: false,
            src: "",
            sliderSrc: "",
            startSlidingTime: undefined,
            init: function () {
                let captchaBar = document.querySelector('.captcha__bar');
                captchaBar.style.setProperty('--textColor', '#333')
                captchaBar.style.setProperty('--width', Math.floor($('.captcha').width() / 2) + 'px')
                captchaBar.style.setProperty('--pwidth', -Math.floor($('.captcha').width() / 2) + 'px')
            },
            startRequestGenerate: function () {
                this.reset();
                $("#showGenerateLoadding").show();
            },
            endRequestGenerate: function (src, sliderSrc) {
                $("#showGenerateLoadding").hide();
                this.src = src;
                this.sliderSrc = sliderSrc;
                $("#captchaSrc").attr('src', src);
                $("#sliderSrc").attr('src', sliderSrc);
            },
            startRequestVerify: function () {
                $("#showVerifyLoadding").show();
            },
            handleDragStart: function (e) {
                let that = this;
                if (!that.isPassing && that.src && that.sliderSrc && !that.isFinish) {
                    window.addEventListener('touchmove', that.handleDragMoving)
                    window.addEventListener('touchend', that.handleDragFinish)
                    window.addEventListener('mousemove', that.handleDragMoving)
                    window.addEventListener('mouseup', that.handleDragFinish)
                    that.isMoving = true;
                    that.startSlidingTime = new Date();
                    that.x = e.pageX || e.touches[0].pageX;
                    that.y = e.pageY || e.touches[0].pageY;
                }
            },
            endRequestVerify: function (isPassing) {
                this.isPassing = isPassing;
                $("#showVerifyLoadding").hide();
                $("#showVerifyTip").show();
                if (isPassing) {
                    $("#isPassing").show();
                } else {
                    $("#isFail").show();
                }
            },
            handleDragMoving: function (e) {
                if (captcha.isMoving && !captcha.isPassing && captcha.src && captcha.sliderSrc && !captcha.isFinish) {
                    const _x = (e.pageX || e.touches[0].pageX) - captcha.x
                    const _y = (e.pageY || e.touches[0].pageY) - captcha.y
                    $('.captcha_handler').css('left', _x + 'px');
                    $('.captcha_progress_bar').css('width', _x + 20 + 'px');
                    $('#sliderSrc').css('left', _x + 'px');
                    captcha.tracks.push({
                        x: Math.round(_x),
                        y: Math.round(_y),
                        t: new Date().getTime() - captcha.startSlidingTime.getTime()
                    })
                }
            },
            handleDragFinish: function () {
                if (captcha.isMoving && !captcha.isPassing && captcha.src && captcha.sliderSrc && !captcha.isFinish) {
                    captcha.isMoving = false;
                    captcha.isFinish = true;
                    captcha.removeEventListeners();
                    handleFinish({
                        backgroundImageWidth: $("#captchaSrc").get(0).offsetWidth,
                        backgroundImageHeight: $("#captchaSrc").get(0).offsetHeight,
                        sliderImageWidth: $("#sliderSrc").get(0).offsetWidth,
                        sliderImageHeight: $("#sliderSrc").get(0).offsetHeight,
                        startTime: captcha.startSlidingTime,
                        endTime: new Date(),
                        tracks: captcha.tracks
                    })
                }
            },
            reset: function () {
                this.x = 0;
                this.y = 0;
                this.tracks = [];
                this.isPassing = false;
                this.isFinish = false;
                this.isMoving = false;
                $("#showGenerateLoadding").hide();
                $("#showVerifyLoadding").hide();
                $("#showVerifyTip").hide();
                $("#isPassing").hide();
                $("#isFail").hide();
                $(".captcha_progress_bar").css('width', "0");
                $(".captcha_slider").css('left', 0);
                $(".captcha_handler").css('left', 0);
            },
            removeEventListeners: function () {
                let that = this;
                window.removeEventListener('touchmove', that.handleDragMoving)
                window.removeEventListener('touchend', that.handleDragFinish)
                window.removeEventListener('mousemove', that.handleDragMoving)
                window.removeEventListener('mouseup', that.handleDragFinish)
            }
        }
        //生成验证码
        function generate() {
            captcha.init();
            captcha.startRequestGenerate();
            $.get('/home/captcha', function (result) {
                if (result.success) {
                    requestId = result.data.id;
                    captcha.endRequestGenerate(result.data.backgroundImage, result.data.sliderImage);
                } else {
                    captcha.endRequestGenerate(null, null)
                }
            })
        }
        //验证码校验
        function handleFinish(data) {
            captcha.startRequestVerify();
            let times = data.endTime - data.startTime;
            let seconds = Math.floor(((times / 1000) % 60) * 100) / 100;
            $.ajax({
                type: "post",
                url: "/home/validate",
                dataType: "json",
                data: {
                    "id": requestId,
                    "track": data
                },
                success: function (result) {
                    if (result.success) {
                        $("#resultTipMessage").html("验证通过,用时" + seconds + "秒");
                        captcha.endRequestVerify(result.success);
    
    
                    } else {
                        $("#resultTipMessage").html("验证未通过,拖动滑块将悬浮图像正确合并");
                        generate();
                    }
                }
            })
    
    
        }
        //使用
        $(document).ready(function () {
            generate();
        })
        //点击关闭按钮
        $(".closeCaptcha").on('click', function () {
           console.log("关闭验证码框");
           captcha.endRequestGenerate(null, null)
            $('.captcha').hide();
        })
        //点击刷新按钮
        $(".refreshCaptcha").on('click', function () {
            generate();
        })
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165

    内容不算复杂,经过快1个月的时间,滑动验证码所有部分都完成了。感谢大家的关注!文笔不好,写的内容都是属于直接上代码,看懂看不懂靠自己的那种-_-! 其实,对于新手而言,仔细琢磨一下,对于以后也是很有帮助的。

    下载方式:
    点击下方公众号卡片,关注我,回复captcha 免费领取!

  • 相关阅读:
    计算机毕业设计Java爱宠医院管理系统(源码+系统+mysql数据库+lw文档)
    活动推广的作用有哪些呢?
    JAVA计算机毕业设计校园统一网络授课平台系统Mybatis+源码+数据库+lw文档+系统+调试部署
    开源更安全? yum源配置/rpm 什么是SSH?
    【C++】STL08关联容器-map
    数据结构练习-算法与时间复杂度
    力扣经典150题第四十二题:字母异位词分组
    Vue项目文件导入、导出
    Python字符串的运算及转义字符
    Jmeter(五十三) - 从入门到精通高级篇 - 懒人教你在Linux系统中安装Jmeter(详解教程)
  • 原文地址:https://blog.csdn.net/sd2208464/article/details/127993825