Bootstrap

uniapp、vue实现滑动拼图验证码

https://blog.51cto.com/u_15930000/7324703

父组件:

<button @click="handleOpen">打开滑块弹窗</button>
<sliderVerify ref="sliderVerifyRef" @verifyCode="getVefCodeTrue"></sliderVerify>

import sliderVerify from './sliderVerify.vue'

handleLogin(){
		this.$refs.sliderVerifyRef.getSlideBlockApi()
}
// 滑块验证结果回调函数
getVefCodeTrue(captcha){
    console.log(captcha)
}

sliderVerify.vue文件

<template>
	<view class="frame--dialog">
		<view class="frameBg" v-show="showModal" style="z-index:10"></view>
		<view class="framework" v-if="isVefCode">
			<view class="boxTopTitle">
				<text>请拖动滑块完成拼图</text>
				<uni-icons type="closeempty" size="20" @click="handleCloseRef"></uni-icons>
			</view>
			<view class="boxImg">
				<view class="cutImgSet" :class="isAnimation?'animation':''" :style="{top:CutImgY+'px', left: blockLeft + 'px'}">
					<image :src="CutImg" style="cursor:pointer; z-index: 10;"
						:style="{width:cutWidth+'px',height:cutHeight+'px'}"></image>
				</view>
				<image :src="BGImg" style="border-radius:8px; width: 320px;height: 155px;"></image>
				<view class="reset" @click="getSlideBlockApi">
					<uni-icons type="refreshempty" size="30"></uni-icons>
				</view>
			</view>
			<view class="checkBox">
				<view class="checkBar">
					<view class="slide">
						<view :class="'moveBac '+(isAnimation?' animation':'')" :style="'width:'+blockLeft+'px;'"></view>
						<view class="successBlock" :class="'swiperBlock '+(isAnimation?' animation':'') "
							:style="'left:'+blockLeft+'px'" ref="sliderBtn" @touchstart="handleTouchstart"
							@touchmove.stop.prevent="handleTouchmove" @touchend="handleTouchend">
							<uni-icons type="arrow-right" size="30" color="#ffffff"></uni-icons>
						</view>
					</view>
				</view>
			</view>
		</view>
	</view>
</template>
<script>
	import {
		getSlider
	} from "./api.js";
	export default {
		props: {
			theme: {
				type: String,
			},
			barWidth: {
				type: Number,
				default: 300
			}
		},
		data() {
			return {
				BGImg: '',
				CutImg: '',
				CutImgY: '',
				isVefCode: false,
				showModal: false,
				startInfo: {},
				blockLeft: 0, //随机拼图的最终X轴距离
				isAnimation: false,
				cutWidth: 65,
				cutHeight: 55,
				nonceStr: ''
			}
		},
		computed: {
			trueMoveableW: function() {
				return this.barWidth - 40
			}
		},
		methods: {
      //初始化
			async getSlideBlockApi() {
				let res = await getSlider({});
				if (res?.code == 200) {
					// nonceStr.value = res.data.nonceStr;
					// return res.data;
					this.BGImg = res.data.canvasSrc; //大图
					this.CutImg = res.data.blockSrc; //拼图
					this.CutImgY = res.data.blockY; //接口位置
					this.cutWidth = res.data.blockWidth;//拼图宽度
					this.cutHeight = res.data.blockHeight;//拼图高度
					this.handleOpenRef()
				}
			},

			//手指按下
			handleTouchstart({
				changedTouches
			}) {
				this.isAnimation = false
				this.startInfo = changedTouches[0]
			},
			// 手指移动
			handleTouchmove({
				changedTouches
			}) {
				let blockLeft = changedTouches[0].clientX - this.startInfo.clientX
				let blockLeftRpx = blockLeft;
				if (blockLeftRpx < 0) {
					this.blockLeft = 0
				} else {
					this.blockLeft = blockLeftRpx <= this.trueMoveableW ? blockLeftRpx : this.trueMoveableW
				}
			},
			// 手指离开
			handleTouchend(e) {
				this.$emit('verifyCode', {
					value: this.blockLeft
				})
			},
			/* 鼠标滑块移动 */
			moving(e) {
				let self = this;
				e = e || window.event;
				let moveX = (e.pageX || e.targetTouches[0].pageX);

				let d = moveX - self.moveStart;

				let w = self.dataWidth;
				let PL_Size = this.puzzleSize;
				let padding = this.padding;

				if (self.moveStart === "") {
					return "";
				}
				if (d < 0 || d > w - padding - PL_Size) {
					return "";
				}

				if (d <= 260) {
					self.blockLeft = d
				}
			},
			handleCloseRef() {
				this.showModal = false;
				this.isVefCode = false;
				// 重置滑块位置
				this.blockLeft = 0;
			},
			handleOpenRef(msg) {
				this.showModal = true;
				this.isVefCode = true;
			},
		}
	}
</script>
<style lang="scss" scoped="scoped">
	.framework {
		box-sizing: border-box;
		width: 350px;
		height: 270px;
		background: #fff;
		margin: 24px auto;
		z-index: 11;
		position: relative;
		padding: 0 15px;
		user-select: none;
		border-radius: 16px;
	}

	.framework .boxTopTitle {
		height: 48px;
		line-height: 48px;
		display: flex;
		justify-content: space-between;
	}

	.framework .boxImg {
		height: 150px;
		background: #fff;
		margin-bottom: 16px;
		border-radius: 8px;
		position: relative;
	}

	.reset {
		position: absolute;
		top: 0;
		right: 0;
		padding: 0 8px;
		line-height: 32px;
	}

	.cutImgSet {
		position: absolute;
	}

	.frame--dialog {
		.framework {
			z-index: 88888888;
			position: fixed;
			top: 25vh;
			transform: translateX(-50%);
			left: 50%;
		}

		.frameBg {
			position: fixed;
			top: 0;
			left: 0;
			width: 100%;
			height: 100%;
			background-color: rgba(0, 0, 0, 0.24);
			z-index: 8887 !important;
		}
	}

	.checkBox .checkBar {
		width: 100%;
		padding: 0px;
	}

	.slide {
		box-sizing: border-box;
		width: 100%;
		height: 40px;
		line-height: 40px;
		border-radius: 8px;
		background-color: #FFFFFF;
		position: relative;
		font-size: 13px;
		overflow: hidden;
		border: 1px solid rgba(65, 157, 231, 0.56);
	}

	.moveBac {
		background-color: rgba(21, 132, 223, 0.08);
		width: 100%;
		height: 100%;
	}
	.swiperBlock {
		width: 40px;
		height: 40px;
		border-radius: 8px;
		display: flex;
		justify-content: center;
		align-items: center;
		position: absolute;
		left: 0px;
		top: -1px;
	}

	.successBlock {
		background-color: #0076D6;
	}

	.animation {
		transition: all 0.5s;
	}


</style>

;