使用vue3+jQuery实现滑动验证码的前端部分

1.效果图

 2.整体思路是:点登录时触发验证码索取接口取到图片,滑动验证后返回数据给后端验证是否成功(本来做了个纯前端的滑动验证,但是不复合安全性要求被打回去重做了)

<template>
	<div class="slider">
		<div class="content">
			<div class="slider-img-div" id="slider-img-div">
				<img id="slider-img" src="" alt />
			</div>
			<div class="bg-img-div"></div>
		</div>
		<div class="slider-move">
			<div class="slider-move-track">拖动滑块完成拼图</div>
			<div class="slider-move-btn" id="slider-move-btn"></div>
		</div>
		<div class="bottom">
			<div class="close-btn" @click="closeClick" id="slider-close-btn"></div>
			<div class="refresh-btn" @click="refreshCaptcha" id="slider-refresh-btn"></div>
		</div>
	</div>
</template>

<script>
	import { defineComponent, ref, onMounted } from 'vue'
	import dayjs from 'dayjs'
	import { message } from 'ant-design-vue'

	export default defineComponent({
		props: {
			isShow: {
				type: Boolean
			}
		},
		setup(props, context) {
			let currentCaptchaConfig = null
			const currentCaptchaId = ref(null)
			const noticeText = ref(null)
			const clearPreventDefault = (event) => {
				if (event.preventDefault) {
					event.preventDefault()
				}
			}
			// 清除事件的默认行为。
			const clearAllPreventDefault = ($div) => {
				$div.each(function (index, el) {
					el.addEventListener('touchmove', clearPreventDefault, false)
				})
			}
			// 使用滑动条的时间格式
			const formaTime = (time) => {
				return dayjs(time).format('YYYY-MM-DD HH:mm:ss')
			}
			// 初始化加载配置
			const initConfig = (bgImageWidth, bgImageHeight, sliderImageWidth, sliderImageHeight, end) => {
				currentCaptchaConfig = {
					startTime: new Date(),
					trackArr: [],
					movePercent: 0,
					bgImageWidth, // 背景大图宽度
					bgImageHeight, // 背景大图高度
					sliderImageWidth, // 滑动的小图的宽度
					sliderImageHeight, // 滑动的小图的高度
					end
				}
				return currentCaptchaConfig
			}
			// 重置操作
			const refreshCaptcha = () => {
				// jQuery请求接口方式
				$.get('/api/captcha/gen', function (data) {
					reset()
					currentCaptchaId.value = data.data.id // 获取验证码id
					const bgImg = $('.bg-img-div')
					const sliderImg = $('.slider-img-div')
					// 通过接口取值给背景图和滑动图块进行初始化
					bgImg.css('background-image', 'url(' + data.data.captcha.backgroundImage + ')')
					bgImg.css('width', data.data.captcha.backgroundImageWidth / 2.27)
					sliderImg.css('background-image', 'url(' + data.data.captcha.templateImage + ')')
					sliderImg.css('width', data.data.captcha.templateImageWidth / 2.27)
					initConfig(bgImg.width(), bgImg.height(), sliderImg.width(), sliderImg.height(), 206)
				})
			}

			const reset = () => {
				// 重置时默认将滑动条恢复到初始位置
				$('#slider-move-btn').css('background-position', '-5px 11.79625%')
				$('#slider-move-btn').css('transform', 'translate(0px, 0px)')
				$('#slider-img-div').css('transform', 'translate(0px, 0px)')
				currentCaptchaId.value = null
			}

			onMounted(() => {
				refreshCaptcha()
				clearAllPreventDefault($('.slider'))
				$('#slider-move-btn').mousedown(down)
			})
			// 鼠标点击触发事件
			const down = (event) => {
				let targetTouches = event.originalEvent ? event.originalEvent.targetTouches : event.targetTouches
				let startX = event.pageX
				let startY = event.pageY
				if (startX === undefined) {
					startX = Math.round(targetTouches[0].pageX)
					startY = Math.round(targetTouches[0].pageY)
				}
				currentCaptchaConfig.startX = startX
				currentCaptchaConfig.startY = startY

				const pageX = currentCaptchaConfig.startX
				const pageY = currentCaptchaConfig.startY
				const startTime = currentCaptchaConfig.startTime
				const trackArr = currentCaptchaConfig.trackArr
				trackArr.push({
					x: pageX - startX,
					y: pageY - startY,
					type: 'down',
					t: new Date().getTime() - startTime.getTime()
				})
				// printLog('start', startX, startY)
				// pc
				window.addEventListener('mousemove', move)
				window.addEventListener('mouseup', up)
				// 手机端
				window.addEventListener('touchmove', move, false)
				window.addEventListener('touchend', up, false)
				doDown(currentCaptchaConfig)
			}
			const doDown = () => {
				$('#slider-move-btn').css('background-position', '-5px 31.0092%')
			}
			// 点击滑块移动触发事件
			const move = (event) => {
				if (event instanceof TouchEvent) {
					event = event.touches[0]
				}
				let pageX = Math.round(event.pageX)
				let pageY = Math.round(event.pageY)
				const startX = currentCaptchaConfig.startX
				const startY = currentCaptchaConfig.startY
				const startTime = currentCaptchaConfig.startTime
				const end = currentCaptchaConfig.end
				const bgImageWidth = currentCaptchaConfig.bgImageWidth
				const trackArr = currentCaptchaConfig.trackArr
				let moveX = pageX - startX
				const track = {
					x: pageX - startX,
					y: pageY - startY,
					type: 'move',
					t: new Date().getTime() - startTime.getTime()
				}
				trackArr.push(track)
				if (moveX < 0) {
					moveX = 0
				} else if (moveX > end) {
					moveX = end
				}
				currentCaptchaConfig.moveX = moveX
				currentCaptchaConfig.movePercent = moveX / bgImageWidth
				doMove(currentCaptchaConfig)
				// printLog('move', track)
			}
			const doMove = (currentCaptchaConfig) => {
				const moveX = currentCaptchaConfig.moveX
				$('#slider-move-btn').css('transform', 'translate(' + moveX + 'px, 0px)')
				$('#slider-img-div').css('transform', 'translate(' + moveX + 'px, 0px)')
			}
			// 滑动图块松开后触发事件
			const up = (event) => {
				window.removeEventListener('mousemove', move)
				window.removeEventListener('mouseup', up)
				window.removeEventListener('touchmove', move)
				window.removeEventListener('touchend', up)
				if (event instanceof TouchEvent) {
					event = event.changedTouches[0]
				}
				currentCaptchaConfig.stopTime = new Date()
				let pageX = Math.round(event.pageX)
				let pageY = Math.round(event.pageY)
				const startX = currentCaptchaConfig.startX
				const startY = currentCaptchaConfig.startY
				const startTime = currentCaptchaConfig.startTime
				const trackArr = currentCaptchaConfig.trackArr

				const track = {
					x: pageX - startX,
					y: pageY - startY,
					type: 'up',
					t: new Date().getTime() - startTime.getTime()
				}

				trackArr.push(track)
				// printLog('up', track)
				valid(currentCaptchaConfig)
			}

			$('#slider-move-btn').mousedown(down)
			$('#slider-move-btn').on('touchstart', down)
			const valid = (captchaConfig) => {
				let data = {
					bgImageWidth: captchaConfig.bgImageWidth,
					bgImageHeight: captchaConfig.bgImageHeight,
					templaterImageWidth: captchaConfig.templateImageWidth,
					templateImageHeight: captchaConfig.templateImageHeight,
					//startSlidingTime: `${captchaConfig.startTime}-${month}-${day} ${hours}:${minutes}:${seconds}`
					startSlidingTime: formaTime(captchaConfig.startTime),
					entSlidingTime: formaTime(captchaConfig.stopTime),
					trackList: captchaConfig.trackArr
				}
				// console.log(currentCaptchaId.value)
				$.ajax({
					type: 'POST',
					url: '/api/captcha/check?id=' + currentCaptchaId.value,
					contentType: 'application/json',
					dataType: 'json',
					data: JSON.stringify(data),
					success: function (res) {
						// console.log(res)
						if (res.data) {
							// message.success('验证成功')
							// noticeText.value = '验证成功'
							context.emit('verify', false)
							context.emit('continue', true)
						} else {
							// message.warning('请重新验证')
							// noticeText.value = '请重新验证'
						}
						refreshCaptcha()
					}
				})
			}

			const closeClick = () => {
				context.emit('verify', false)
			}
			return {
				currentCaptchaId,
				refreshCaptcha,
				reset,
				down,
				doDown,
				move,
				doMove,
				up,
				valid,
				closeClick,
				noticeText
			}
		}
	})
</script>
<style scoped>
	.slider {
		background-color: #fff;
		/* width: 278px; */
		height: 285px;
		z-index: 999;
		box-sizing: border-box;
		padding: 9px;
		border-radius: 6px;
		box-shadow: 0 0 11px 0 #999999;
	}

	.slider .content {
		width: 100%;
		height: 159px;
		position: relative;
	}

	.bg-img-div {
		width: 100%;
		height: 100%;
		position: absolute;
		transform: translate(0px, 0px);
		background-size: 100% 159px;
		background-image: none;
		background-position: 0 0;
		z-index: 0;
	}

	.slider-img-div {
		height: 100%;
		width: 100%;
		background-size: 100% 159px;
		position: absolute;
		transform: translate(0px, 0px);
		/*border-bottom: 1px solid blue;*/
		z-index: 1;
	}

	.bg-img-div img {
		width: 100%;
	}

	.slider-img-div img {
		height: 100%;
	}

	.slider .slider-move {
		height: 60px;
		width: 100%;
		margin: 11px 0;
		position: relative;
	}

	.slider .bottom {
		height: 19px;
		width: 100%;
	}

	.refresh-btn,
	.close-btn,
	.slider-move-track,
	.slider-move-btn {
		background: url(https://static.geetest.com/static/ant/sprite.1.2.4.png) no-repeat;
	}

	.refresh-btn,
	.close-btn {
		display: inline-block;
	}

	.slider-move .slider-move-track {
		/* background-size: 100% auto; */
		line-height: 38px;
		font-size: 14px;
		text-align: center;
		white-space: nowrap;
		color: #88949d;
		-moz-user-select: none;
		-webkit-user-select: none;
		user-select: none;
	}

	.slider {
		user-select: none;
	}

	.slider-move .slider-move-btn {
		transform: translate(0px, 0px);
		background-position: -5px 11.79625%;
		position: absolute;
		top: -12px;
		left: 0;
		width: 66px;
		height: 66px;
	}
	.slider-move .slider-text {
		margin: 9px 0;
		color: red;
	}

	.slider-move-btn:hover,
	.close-btn:hover,
	.refresh-btn:hover {
		cursor: pointer;
	}

	.bottom .close-btn {
		width: 20px;
		height: 20px;
		margin-right: 20px;
		background-position: 0 44.86874%;
	}

	.bottom .refresh-btn {
		width: 20px;
		height: 20px;
		background-position: 0 81.38425%;
	}
</style>

3.后端接口设计参照

tianai-captcha-demo: 滑块验证码demo - Gitee.com

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值