uniapp y vue implementan código de verificación de rompecabezas deslizante

uniapp y vue implementan código de verificación de rompecabezas deslizante


En el trabajo de desarrollo real, se requiere un código de verificación por SMS al iniciar sesión, pero es fácil provocar un comportamiento del rastreador, por lo que se debe utilizar un código de verificación anti-rastreador. Hoy presentaré el código de verificación del rompecabezas para resolver el código de verificación deslizante anti- rastreador en código de verificación anti-rastreador. El código de verificación del rompecabezas deslizante agrega una distancia de deslizamiento aleatoria al código de verificación del bloque deslizante. El usuario debe deslizar el control deslizante hasta el espacio en el rompecabezas para completarlo y pasar la verificación. Compatibilidad de plataforma, H5, subprograma WeChat, Byte, Baidu, QQ, etc.

En el trabajo de desarrollo real, se requiere un código de verificación por SMS al iniciar sesión, pero es fácil provocar un comportamiento del rastreador, por lo que se debe utilizar un código de verificación anti-rastreador. Hoy presentaré el código de verificación del rompecabezas para resolver el código de verificación deslizante anti- rastreador en código de verificación anti-rastreador.
Principio:
el código de verificación del rompecabezas deslizante agrega una distancia de deslizamiento aleatoria al código de verificación del bloque deslizante. El usuario debe deslizar el control deslizante hasta el espacio del rompecabezas para completarlo y pasar la verificación.


1. Versión de interfaz incorrecta

Compatibilidad de plataforma, H5, subprograma WeChat, Byte, Baidu
XXX. El archivo vue se introduce
en la etiqueta de plantilla

<slider-verify :isShow="sliderVerifyFLag" @touchSliderResult="verifyResult" ref="verifyElement"></slider-verify>

Introducir componentes en etiquetas de script

import sliderVerify from '@/components/slider-verify/slider-verify.vue';//组件存放的路径

export default {
    components: {
        'slider-verify': sliderVerify
    },
    data() {
        return {
            sliderVerifyFLag: false //滑块验证
        };
    },
    onLoad() {},
    methods: {
        // 滑块验证结果回调函数
        verifyResult(res) {
            this.sliderVerifyFLag = false;

            if (res) {  //校验通过

            }else{
                // 校验失败,点击关闭按钮
            }
        }
    }
};

Insertar descripción de la imagen aquí
código de archivo slider-verify.vue

<template>
	<view class="slider-verify-box" v-if="isShow">
		<view class="verifyBox">
			<view class="slider-title">图形验证</view>
			<view class="slide-content">
				<view class="slide-tips">拖动下方滑块完成拼图</view>
				<view class="slider-pintu">
					<image id="pintuImg" :src="'/static/images/slider-verify/' + img + '.jpg'" class="pintu"></image>

					<view class="pintukuai" :style="{ top: top + 'px', left: oldx + 'px' }">
						<image :src="'/static/images/slider-verify/' + img + '.jpg'" :style="{ top: '-' + top + 'px', left: '-' + left + 'px'}"></image>
					</view>

					<view class="yinying" :style="{ top: top + 'px', left: left + 'px' }"></view>
				</view>

				<view class="slider-movearea" @touchend="endTouchMove">
					<movable-area :animation="true"><movable-view :x="x" direction="horizontal" @change="startMove"></movable-view></movable-area>

					<view class="huadao">拖动左边滑块完成上方拼图</view>
				</view>
			</view>
			
			<view class="slider-btn-group">
				<view class="slider-btn" @tap="closeSlider">关闭</view>
				<view class="slider-btn slide-btn-refresh" @tap="refreshVerify">刷新</view>
			</view>
		</view>
	</view>
</template>

<script>
export default {
	name: 'slider-verify',
	props: {
		isShow: true
	},
	data() {
		return {
			x: 0, //初始距离
			oldx: 0, //移动的距离
			img: '1', //显示哪张图片
			left: 0, //随机拼图的最终X轴距离
			top: 0, //拼图的top距离
		};
	},
	watch: {
		// 每次打开重新刷新拼图
		isShow(newValue, oldValue) {
			if(newValue){
				this.refreshVerify();	//刷新
			}
		}
	},
	mounted() {
		var that = this;
		that.refreshVerify();
	},
	methods: {
		//刷新验证
		refreshVerify() {
			var gl = Math.random().toFixed(2);
			this.left = uni.upx2px(560) * gl > uni.upx2px(280) ? uni.upx2px(280) : uni.upx2px(560) * gl + uni.upx2px(150); //生成随机X轴最终距离
			this.top = uni.upx2px(190) * gl; //生成随机Y轴初始距离
			
			if (gl <= 0.2) {
				this.img = 1;
			}
			if (gl > 0.2 && gl <= 0.4) {
				this.img = 2;
			}
			if (gl > 0.4 && gl <= 0.6) {
				this.img = 3;
			}
			if (gl > 0.6 && gl <= 0.8) {
				this.img = 4;
			}
			if (gl > 0.8 && gl <= 1) {
				this.img = 5;
			}
			this.resetMove();	//重置阴影位置
		},

		/* 滑动中 */
		startMove(e) {
			this.oldx = e.detail.x;
		},

		/* 滑动结束 */
		endTouchMove() {
			var that = this;

			if (Math.abs(that.oldx - that.left) <= 5) {
				uni.showToast({
					title: '验证成功',
					duration: 2500,
					success() {
						that.$emit('touchSliderResult', true);
					}
				});
			} else {
				that.refreshVerify();
			}
		},
		
		/* 重置阴影位置 */
		resetMove() {
			this.x = 1;
			this.oldx = 1;
			setTimeout(() => {
				this.x = 0;
				this.oldx = 0;
			}, 300);
		},
		
		// 关闭
		closeSlider(){
			this.$emit('touchSliderResult', false);
		}	
	}
};
</script>

<style lang="less">
.slider-verify-box {
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	background-color: rgba(0, 0, 0, 0.5);
	z-index: 999;
}
.verifyBox {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	// width: 85%;
	background-color: #fff;
	border-radius: 20upx;
	box-shadow: 0 0 5upx rgba(0, 0, 0);

	.slider-title {
		font-size: 36upx;
		text-align: center;
		padding: 1em 0;
		color: rgba(2, 20, 33, 0.85);
		border-bottom: 1px solid rgba(2, 20, 33, 0.15);
	}

	.slide-content {
		width: 560rpx;
		padding: 0 1em;
		margin: 0 auto;
		.slide-tips {
			font-size: 28rpx;
			color: rgba(2, 20, 33, 0.45);
			padding: 0.5em 0;
		}

		.slider-pintu {
			position: relative;
			width: 100%;
			border-radius: 10rpx;
			overflow: hidden;
			.pintu {
				width: 560rpx;
				height: 315rpx;
				display: block;
				margin: 0 auto;
			}

			.pintukuai {
				position: absolute;
				top: 0;
				left: 0;
				width: 120rpx;
				height: 120rpx;
				z-index: 100;
				box-shadow: 0 0 5upx rgba(0, 0, 0, 0.3);
				overflow: hidden;

				image {
					display: block;
					position: absolute;
					top: 0;
					left: 0;
					width: 560rpx;
					height: 315rpx;
				}
			}
		}

		.yinying {
			position: absolute;
			width: 120rpx;
			height: 120rpx;
			background-color: rgba(0, 0, 0, 0.5);
		}
	}
}

.slider-movearea {
	position: relative;
	height: 80upx;
	width: 100%;
	margin: 25upx auto;

	movable-area {
		height: 80upx;
		width: 100%;

		movable-view {
			width: 80upx;
			height: 80upx;
			border-radius: 50%;
			background-color: #007cff;
			background-image: url(../../static/images/slider-verify/icon-button-normal.png);
			background-repeat: no-repeat;
			background-size: auto 30upx;
			background-position: center;
			position: relative;
			z-index: 100;
		}
	}
}

.huadao {
	width: 100%;
	height: 66upx;
	line-height: 66upx;
	background: #eee;
	box-shadow: inset 0 0 5upx #ccc;
	border-radius: 40rpx;
	color: #999;
	text-align: center;
	box-sizing: border-box;
	position: absolute;
	top: 7rpx;
	left: 0;
	font-size: 28rpx;
	z-index: 99;
}

.slider-btn-group{
	width: 100%;
	display: flex;
	justify-content: center;
	align-items: center;
	border-top: 1px solid rgba(2, 20, 33, 0.15);
	
	.slider-btn{
		flex: 1;
		height: 100rpx;
		line-height: 100rpx;
		text-align: center;
		font-size: 36rpx;
		color:rgba(2,20,33,0.85);
		&:active{
			opacity: 0.8;
		}
	}
	.slide-btn-refresh{
		color:rgba(14,107,176,1);
		border-left: 1px solid rgba(2, 20, 33, 0.15);
	}	
}
</style>

2. Versión de la interfaz
Insertar descripción de la imagen aquí

Compatibilidad de plataforma, H5, subprograma WeChat, Byte, Baidu, qq, etc.
Los archivos XXX.vue se introducen
en la etiqueta de plantilla.

<view @tap="getCode()" >获取验证码</view>
<validationPT class="setVef" ref="vefCode" @VefCodeTrue="getVefCodeTrue" :inputPhone="mobile" theme="dialog"></validationPT>

Introducir componentes en etiquetas de script

import validationPT from '@/components/validationPT/validationPT.vue'

export default {
    components: {
        'slider-verify': sliderVerify
    },
    data() {
       
    },
    onLoad() {},
    methods: {
    	// 效验手机号
		getCode() {
			this.getCodePT();
		},
		//获取弹窗
		getCodePT(msg) {
			this.$refs.vefCode.GetSlideBlockApi(); //调用图片接口,获取验证图片
			this.$refs.vefCode.clkOpenRef(); //验证通过,打开拼图验证
			this.$refs.vefCode.initial();//重置
		},
        // 滑块验证结果回调函数
        //拼图验证是否成功
		getVefCodeTrue(msg) {
			this.VefInfosBk = msg;
			if (msg.Res == true) {
				//获取验证码
			} 
		},
    }
};

validación del código del archivo PT.vue

<template>
	<view :class="[{'frame--dialog':theme}]">
		<view class="frameBg" v-show="showModal" style="z-index:10"></view>
		<view class="framework" v-if="isVefCode">
			<view class="boxTopTitle">
				<text>图形验证码</text>
				<text class="cuIcon-close font16 fontB" @click="clkCloseRef"></text>
			</view>
			<view class="boxImg">
				<view class="cutImgSet" :class="isAnimation?'animation':''"
					:style="{top:CutImgY+'px', left: blockLeft + 'px'}">
					<image :src="CutImg" style="cursor:pointer; width: 40px;height: 40px;z-index: 10;"></image>
				</view>
				<image :src="BGImg" style="border-radius:8px; width: 300px;height: 150px;"></image>
				<view class="reset" @click="clkNext">
					<text class="cuIcon-refresh font16 text-white"></text>
				</view>
			</view>
			<view class="checkBox">
				<view class="checkBar">
					<view class="slide" :class="bgColSet?'active':''">
						<view :class="'moveBac '+(isAnimation?' animation':'')+(bgColSet?' moveBacError':'')"
							:style="'width:'+(isSuccess ? 300 : blockLeft)+'px;'"></view>
						<view :class="(bgColSet?'swiperTipsError':'swiperTips') + (!isSuccess?' paddingL40':'')">
							<text v-if="!isSuccess">请拖动滑块验证</text>
							<text v-if="isSuccess" class="cuIcon-check margin-right-sm fontB"></text>
							<text v-if="isSuccess">验证成功</text>
						</view>
						<view v-if="!isSuccess"
							:class="'swiperBlock '+(bgColSet?' errorBlock':' successBlock')+(isAnimation?' animation':'') "
							:style="'left:'+blockLeft+'px'" ref="sliderBtn" @touchstart="touchstartHandle"
							@mousedown="startMove" @touchmove.stop.prevent="touchmoveHandle"
							@touchend="touchendHandle">
							<!-- #ifndef MP-BAIDU -->
							<image v-if="!bgColSet" :src="ImgUrl + 'Images/doubleArrow.svg'" mode="widthFix"
								style="width: 16px;"></image>
							<text v-else class="cuIcon-close font16 text-white"></text>
							<!-- #endif -->
							<!-- #ifdef MP-BAIDU -->
							<text v-if="bgColSet" class="cuIcon-close font16 text-white"></text>
							<!-- #endif -->
						</view>
					</view>
				</view>
			</view>

		</view>
	</view>
</template>
<script>
	export default {
		props: {
			inputPhone: '',
			theme: {
				type: String,
			},
			swiperColor: {
				type: String,
				default: 'rgba(21, 132, 223, 0.08)'
			},
			title: {
				type: String,
				default: '人机校验'
			},
			barWidth: {
				type: Number,
				default: 300
			}
		},
		data() {
			return {
				ImgUrl: this.hostwapUrl,
				BGImg: '',
				CutImg: '',
				CutImgY: '',
				MarkCode: '', //验证拼图是否成功用,
				bgColSet: false, //拼图是否验证成功
				isSuccess: false, //是否验证成功
				isVefCode: false,
				showModal: false,
				startInfo: {},
				blockLeft: 0,//随机拼图的最终X轴距离
				isAnimation: false,
				msgType: '',
			}
		},
		computed: {
			trueMoveableW: function() {
				return this.barWidth - 40
			}
		},
		methods: {
			//获取图片接口
			GetSlideBlockApi() {
				let that = this;
				uni.request({
					url: url,//获取拼图接口
					data: data,//需要传给接口的参数
					headers: {
						Accept: "application/json; charset=utf-8"
					},
					dataType: 'json',
					method: 'GET',
					success: (res) => {
						this.BGImg = '';//大图
						this.CutImg = '';//拼图
						this.CutImgY = 0;//接口位置
					},
					complete: () => {}
				})
			},

			//手指按下
			touchstartHandle({
				changedTouches
			}) {
				if (this.isSuccess) {
					return
				}
				this.isAnimation = false
				this.startInfo = changedTouches[0]
			},
			// 手指移动
			touchmoveHandle({
				changedTouches
			}) {
				if (this.isSuccess) {
					return
				}
				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
				}
			},
			// 手指离开
			touchendHandle(e) {
				if (this.isSuccess) {
					return
				}
				this.CheckSlideBlockApi();
			},
			//验证图片接口
			CheckSlideBlockApi() {
				let that = this;
				uni.request({
					url: url,//接口名称
					data: data,//接口需要的参数
					headers: {
						Accept: "application/json; charset=utf-8"
					},
					dataType: 'json',
					method: 'POST',
					success: (res) => {
						let infos = {}
						if (res.data.IsOK == true) {//成功
							that.isSuccess = true;
							infos = {
								Res: res.data.Results,
							};//返回传给父组件的值,判断是否成功
							setTimeout(() => {
								that.bgColSet = false;
								that.$emit('VefCodeTrue', infos)
								that.isVefCode = false;
								that.showModal = false;
							}, 1000);

						} else {
							that.bgColSet = true;
							that.isSuccess = false;
							that.GetSlideBlockApi();
							that.isAnimation = true
							let timeid = setTimeout(() => {
								clearTimeout(timeid)
								that.isAnimation = false
								that.bgColSet = false;
							}, 500)
							that.blockLeft = 0;
						}
					},
					complete: () => {}
				})
			},
			/* 鼠标按住滑块后初始化移动监听,记录初始位置 */
			startMove(e) {
				e.preventDefault() //禁止图片img拖动的解决方法
				e = e || window.event;
				this.moveStart = e.pageX || e.targetTouches[0].pageX;
				this.addMouseMoveListener();
			},
			/* 鼠标滑块移动 */
			moving(e) {
				// e.preventDefault() //禁止图片img拖动的解决方法
				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
				}
			},
			/* 鼠标移动结束,验证并回调 */
			moveEnd(e) {
				let self = this;

				e = e || window.event;
				let moveEnd_X = (e.pageX || e.changedTouches[0].pageX) - self.moveStart;

				if (moveEnd_X > 0) {
					self.CheckSlideBlockApi(); //验证拼图是否成功
				}
				self.moveStart = "";

				document.removeEventListener("mousemove", self.moving);
				document.removeEventListener("mouseup", self.moveEnd);
			},
			/* 鼠标全局绑定滑块移动与滑动结束,移动过程中鼠标可在页面任何位置 */
			addMouseMoveListener() {
				let self = this;
				document.addEventListener("mousemove", self.moving);
				document.addEventListener("mouseup", self.moveEnd);
			},
			//换一张
			clkNext() {
				this.GetSlideBlockApi();
			},

			// 重置滑块位置
			initial() {
				this.blockLeft = 0;
				this.bgColSet = false;
				this.isSuccess = false;
			},
			clkCloseRef() {
				this.showModal = false;
				this.isVefCode = false;
			},
			clkOpenRef(msg) {
				this.showModal = true;
				this.isVefCode = true;
			},
		}
	}
</script>
<style lang="scss" type="text/css" rel="stylesheet" scoped="scoped">
	.framework {
		box-sizing: border-box;
		width: 332px;
		height: 270px;
		background: #fff;
		margin: 24px auto;
		z-index: 11;
		position: relative;
		padding: 0 16px;
		user-select: none;
		border-radius: 16px;
	}

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

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

	.reset {
		position: absolute;
		top: 0;
		right: 0;
		background-color: rgba(0, 0, 0, 0.24);
		border-radius: 0 8px 0 8px;
		padding: 0 12px;
		line-height: 32px;
	}

	.cutImgSet {
		position: absolute;
	}

	.frame--dialog {
		.framework {
			// margin: 38vh auto;
			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);
	}

	.slide.active {
		border: 1px solid #FA7F7F;
	}

	.moveBac {
		background-color: rgba(21, 132, 223, 0.08);
		width: 100%;
		height: 100%;
	}

	.moveBacError {
		background-color: rgba(216, 63, 63, 0.08) !important;
	}

	.swiperTips {
		box-sizing: border-box;
		position: absolute;
		left: 0;
		top: 0;
		width: 100%;
		color: rgba(255, 255, 255, 0.24);
		text-align: center;
		background: -webkit-gradient(linear, left top, right top, color-stop(0, #0076D6), color-stop(.4, #0076D6), color-stop(.5, #fff), color-stop(.6, #0076D6), color-stop(1, #0076D6));
		animation: tipsBlinkan 3s infinite;
		-webkit-background-clip: text;
		-webkit-text-fill-color: transparent;
		line-height: 40px;
	}

	.swiperTipsError {
		box-sizing: border-box;
		position: absolute;
		left: 0;
		top: 0;
		width: 100%;
		text-align: center;
		color: rgba(255, 255, 255, 0.24);
		background: -webkit-gradient(linear, left top, right top, color-stop(0, #FA7F7F), color-stop(.4, #FA7F7F), color-stop(.5, #fff), color-stop(.6, #FA7F7F), color-stop(1, #FA7F7F));
		animation: tipsBlinkan 3s infinite;
		-webkit-background-clip: text;
		-webkit-text-fill-color: transparent;
		line-height: 40px;
	}

	.swiperBlock {
		width: 40px;
		height: 40px;
		border-radius: 8px;
		/* #ifdef MP-BAIDU */
		background-repeat: no-repeat;
		background-size: 16px;
		background-position: center;
		/* #endif */
		display: flex;
		justify-content: center;
		align-items: center;
		position: absolute;
		left: 0px;
		top: -1px;
	}

	.successBlock {
		background-color: #0076D6;
		/* #ifdef MP-BAIDU */
		background-image: url('https://m.by56.com/Images/doubleArrow.svg');
		/* #endif */
	}

	.errorBlock {
		background-color: #FA7F7F !important;
	}

	.paddingL40 {
		padding-left: 40px;
	}

	.animation {
		transition: all 0.5s;
	}

	@keyframes tipsBlinkan {
		0% {
			background-position: -100px 0;
		}

		100% {
			background-position: 100px 0;
		}
	}
</style>

Supongo que te gusta

Origin blog.csdn.net/meimeieee/article/details/133255621
Recomendado
Clasificación