uniapp 滑动代码块 获取验证码

整体的思路是让ui给你一个整图,然后抠出来一个滑块图,把深色的缺口图,随机放到某一个位置,用滑块的x轴数值去对比缺口的x值,可以留一些空间,验证通过就写别的方法,我这里的y轴是固定的,如果不想,可以试着改改

主页面引用

	<sliderCaptcha v-show="show" :bg="bg" :sky="sky" :slider="slider" closeable closeOnClickOverlay
			:overlayOpacity="0.3" :channelText="'滑动完成验证'" endToStart @change="changeCode" @close="closeCode"
			@refresh="randomNum"></sliderCaptcha>

data里面的数据

bg: {
					src: '../../static/img/20240111103600.png', // 背景图片路径
					width: 240, // 背景图片大小
					height: 150,
					mode: "aspectFit" // 显示模式
				},
				slider: {
					src: '../../static/img/20240111103615.png', // 滑块图片路径
					y: 55, // y轴坐标,这里是根据我自己的后端返回的滑块位置计算方式,根据自己情况而定,如果后端固定y就直接赋值
					width: 40, // 滑块大小
					height: 40
				},
				sky: {
					src: '../../static/img/20240115103902.png', // 滑块图片路径
					y: 50, // y轴坐标,这里是根据我自己的后端返回的滑块位置计算方式,根据自己情况而定,如果后端固定y就直接赋值
					width: 40, // 滑块大小
					height: 40,
					left: 50
				}

method的方法

changeCode({  // 这个是判断滑块和缺口位置重叠的
				x,
				y
			}) {
				if (x > this.sky.left - 12 && x < this.sky.left - 4) {

					uni.showToast({
						title: '验证通过',
						duration: 2000
					});
                   //自己写方法
					this.startCountdown() //获取验证码
					this.closeCode()  //关闭滑块
				} else {
					uni.showToast({
						icon: "error",
						title: '验证未通过',

						duration: 1000
					});

					this.randomNum()


				}
			},




randomNum() { //这个是让缺口的位置随机的
				this.sky.left = this.getRandomTrue(50, 210);
				randomQuery().then(res => {
					this.slider.src = this.$store.state.user.baseUrl + res.puzzle
					this.bg.src = this.$store.state.user.baseUrl + res.underlay
				})
			},

验证码弹出层代码块

<template>
	<view class="code-box" @click="closeOnClickOverlay && closeCodeBox()">
		<view class="overlay-view" :style="{backgroundColor: overlayColor, opacity: overlayOpacity}"></view>
		<view class="container" :style="boxStyle" @click.stop>
			<view class="tips" :style="{color: titleColor}">
				{{title}}
				<uni-icons type="refreshempty" size="18" @click="refresh"></uni-icons>

				<view v-show="closeable" class="close-icon" @click.stop="closeCodeBox">
					<icon color="#bcbcbc" type="cancel" size="26" />
				</view>
			</view>
			<view class="original">
				<image :src="sky.src" mode="" class="hollow" :style="{left: sky.left +'px'}"></image>
				<image :src="bg.src" :mode="bg.mode" :style="originStyle"></image>

				<image :src="slider.src" class="slider-box" :mode="slider.mode"
					:style="{ left: move.left + 'px'}"></image>
			</view>
			<!-- 滑动通道 -->
			<view class="slider-channel">
				<view class="channel1">
					<view class="slider-circle" :style="{left: move.left + 'px'}" @touchend="moveEnd"
						@touchstart="moveStart" @touchmove="moveTouch" >
						<slot name="circle">»</slot>
					</view>
					<view class="channel" >
						<text>{{ channelText }}</text>
						<view class="channel-color" :style="{width: move.left + (circle.width / 2) + 'px', 
              backgroundColor: channelChangeColor, color: changeTextColor}"><text>{{ channelChangeText }}</text></view>
					</view>
				</view>

			</view>
		</view>
	</view>
</template>

<script>
	export default {
		name: "sliderCaptcha",
		data() {
			return {
				channelStyle: { // 滑动管道长度
					width: '100%',
				},
				circle: {
					width: 0,
				},
				move: {
					left: 0
				},
				channelText:  "拖动滑块完成拼图",
				
			};
		},
		mounted() {
			this.$nextTick(async () => {
				let sliderChannel = await this.querySelector('.slider-channel');
				let circle = await this.querySelector('.slider-circle');
				let sliderBox = await this.querySelector('.slider-box');
				let channel1 = await this.querySelector('.channel1');
				// 记录圆宽度
				this.circle.width = circle.width;
				// 记录左边距
				this.move.min = circle.left - channel1.left;
				// 记录右边距
				this.move.max = (this.bg.width || 240) - (this.slider.width || 40);
				// 滑块部分盒子(_channel)宽度:
				//  父盒子宽度(.slider-channel) - 父盒子内边距(20) - 圆形滑块与滑块图片宽度差
				this.channelStyle.width =
					`calc(${sliderChannel.width - 20}px - ${(sliderBox.width - circle.width)}px)`;

				this.move.left = this.move.min;

			})
		},
		methods: {
			moveStart(e) {
				// 记录初始位置
				this.move.offsetLefft = e.touches[0].pageX;
			},
			moveEnd(e) {
				let offset = this.move.left;
				if (this.endToStart)
					this.move.left = this.move.min;
				// 反馈回去
				this.$emit("change", {
					x: offset,
					y: this.slider.y
				});
			},
			moveTouch(e) {
				let moveX = e.touches[0].pageX;
				let left = moveX - this.move.offsetLefft;
				if (left <= this.move.min) {
					left = this.move.min;
				} else if (left >= this.move.max) {
					left = this.move.max;
				}
				this.move.left = left;
			},
			querySelector(name) {
				const query = uni.createSelectorQuery().in(this);
				return new Promise((res) => {
					query.select(name).boundingClientRect(data => {
						res(data);
					}).exec();
				})
			},
			// 点击刷新按钮
			refresh() {
				this.move.left = this.move.min;
				this.$emit("refresh", true);
			},
			// 关闭
			closeCodeBox() {
				this.$emit("close", true);
			},
		},
		props: {
			// 背景原图
			bg: {
				type: Object,
				default () {
					return {
						src: "",
						mode: "",
						width: 240,
						height: 130
					}
				},
				require: true
			},
			sky: {
				type: Object,
				default () {
					return {
						src: "./20240115103902.png",
						y: 0,
						width: 40,
						height: 40,
						mode: '',
						left: 120
					}
				},
				require: true
			},
			// 滑动块
			slider: {
				type: Object,
				default () {
					return {
						src: "",
						y: 0,
						width: 40,
						height: 40,
						mode: ''
					}
				},
				require: true
			},
			// 是否显示关闭按钮
			closeable: {
				type: Boolean,
				default: false,
			},
			// 点击遮罩层关闭弹窗
			closeOnClickOverlay: {
				type: Boolean,
				default: false,
			},
			// 遮罩层颜色
			overlayColor: {
				type: String,
				default: "#ccc"
			},
			// 遮罩层透明度:0-1
			overlayOpacity: {
				type: Number,
				default: .5
			},
			// 标题
			title: {
				type: String,
				default: "请完成下方验证"
			},
			// 标题颜色
			titleColor: {
				type: String,
				default: "black"
			},
			// 滑动管道背景颜色
			channelBG: {
				type: String,
				default: "#d5d5e3"
			},
			// 滑动管道滑动时覆盖掉颜色
			channelChangeColor: {
				type: String,
				default: "#639ef6"
			},
			// 滑动管道内的文字
			
			// 文字颜色
			textColor: {
				type: String,
				default: "white"
			},
			channelChangeText: {
				type: String,
				default: ""
			},
			// 文字颜色
			changeTextColor: {
				type: String,
				default: "white"
			},
			// 滑动验证码框背景
			boxStyle: {
				type: Object,
				default () {
					return {
						backgroundColor: 'white'
					}
				}
			},
			// 结束后回到起点
			endToStart: {
				type: Boolean,
				default: true,
			},
		},
		computed: {
			originStyle() {
				if (!this.bg.src) {
					throw "背景图片路径不能为空";
				}
				return {
					width: (this.bg.width || 240) + 'px',
					height: (this.bg.height || 130) + 'px',
				}
			},
			sliderStyle() {
				if (!this.slider.src) {
					throw "滑块路径不能为空";
				}
				return {
					top: (this.slider.y || 0) + 'px',
					width: (this.slider.width || 40) + 'px',
					height: (this.slider.height || 40) + 'px',
				}
			},
		},
	};
</script>

<style lang='scss' scoped>
	/* @import url("http://at.alicdn.com/t/c/font_4018428_5opbsiglz6g.css"); */
	* {
		box-sizing: content-box;
	}


	.code-box {
		z-index: 1000;
		position: fixed;
		top: 0;
		left: 0;
		width: 100vw;
		height: 100vh;
		background-color: transparent;
		font-size: 14px;

		.overlay-view {
			position: fixed;
			top: 0;
			left: 0;
			z-index: 0;
			width: 100%;
			height: 100%;
		}

		.container {
			overflow: hidden;
			box-sizing: content-box;
			display: flex;
			flex-direction: column;
			justify-content: center;
			position: absolute;
			top: 50%;
			left: 50%;
			padding: 5px;
			transform: translate(-50%, -50%);
			height: auto;
			border-radius: 15px;
			box-shadow: 0 0 5px #7a7a7a;

			.tips {
				padding: 10px;
				text-align: center;
				flex: none;
				position: relative;

				.close-icon {
					position: absolute;
					top: 0px;
					right: 0px;
					overflow: hidden;
					width: 18px;
					height: 18px;
					border-radius: 50%;
					display: flex;
					align-items: center;
					justify-content: center;
				}
			}

			.original {
				position: relative;
				flex: 1;
				margin: 0 5px;
				background-repeat: no-repeat;

				.hollow {
					width: 30px;
					height: 30px;
					position: absolute;
					top: 120rpx !important;
					z-index: 9999 !important;
				}

				.slider-box {
					z-index: 100000 !important;
					position: absolute;
					width: 50px;
					height: 50px;
					top: 104rpx !important;
				}
			}

			.slider-channel {
				

				.channel1 {
					position: relative;
					padding: 10px;

					.slider-circle {
						flex: 1;
						position: absolute;
						z-index: 1000000;
						top: 50%;
						transform: translateY(-50%);
						width: 40px;
						height: 40px;
						line-height: 33px;
						text-align: center;
						font-size: 35px;
						color: #626f82;
						background-color: white;
						border: 1px solid #efefef;
						border-radius: 50%;
						overflow: hidden;
						box-shadow: 0 0 2px #ccc;
					}

					.channel {
						position: relative;
						height: 35px;
						line-height: 35px;
						text-align: center;
						border-radius: 20px;
                        background: #d5d5e3;
						text {
							position: absolute;
							left: 50%;
							width: 100%;
							transform: translateX(-50%);
						}

						.channel-color {
							position: absolute;
							top: 0;
							height: 35px;
							line-height: 35px;
							text-align: center;
							width: 0;
							border-radius: 20px 0 0 20px;
							text-overflow: clip;
							overflow: hidden;

							text {
								position: absolute;
								z-index: 10;
								left: 50%;
								width: 100%;
								transform: translateX(-50%);
							}
						}
					}
				}

			}
		}
	}
</style>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值