officialAccount/compontent/public/huakuai.vue

295 lines
7.1 KiB
Vue

<template>
<view class="captcha-container" ref="container">
<view class="font-title">请通过滑块验证</view>
<view class="captcha-image" style="position: relative; width: 100%; height: 400rpx; overflow: hidden;">
<image :src="bgImage" class="bg-image" mode="widthFix" @load="init" />
<view class="overlay" :style="{width: containerWidth + 'rpx', height: containerHeight + 'rpx'}">
<view class="hole" :style="{
top: originY + 'rpx',
left: (originX +50 ) + 'rpx',
width: pieceSize + 'rpx',
height: pieceSize + 'rpx',
clipPath: clipPath,
transform: 'translate(-50%, -50%)',
backgroundColor: 'rgba(0,0,0,0.6)'
}"></view>
<view class="piece" :style="{
top: originY + 'rpx',
left: (offsetX +50 ) + 'rpx',
width: pieceSize + 'rpx',
height: pieceSize + 'rpx',
backgroundImage: `url(${bgImage})`,
backgroundSize: containerWidth + 'rpx ' + containerHeight + 'rpx',
backgroundPosition: `-${originX+10}rpx -${originY-20}rpx`,
clipPath: clipPath,
transform: 'translate(-50%, -50%)'
}"></view>
</view>
</view>
<view class="slider-bar">
<view class="slider-bar-font">
向右滑动滑块填充拼图
</view>
<view class="slider-button" ref="btn" @touchstart.prevent="onStart" @mousedown.prevent="onStart"
:style="{ left: offsetX + 'rpx', maxWidth: (containerWidth - pieceSize) + 'rpx' }">
<image src="/static/login/right.png" style="width: 50rpx;height: 50rpx;" mode="widthFix" />
</view>
</view>
</view>
</template>
<script setup>
import {
ref,
onBeforeUnmount
} from 'vue';
import {
onLoad
} from '@dcloudio/uni-app';
import img0 from '@/static/login/0.png'
import img1 from '@/static/login/1.png'
import img2 from '@/static/login/2.png'
import img3 from '@/static/login/3.png'
const emit = defineEmits(['success'])
const pieceSizePx = 50;
const pieceSize = pieceSizePx * 2;
const tolerance = 20;
const container = ref(null);
const btn = ref(null);
const containerWidthPx = 200;
const containerHeightPx = 300;
const containerWidth = ref(containerWidthPx * 2);
const containerHeight = ref(containerHeightPx * 2);
const originX = ref(0);
const originY = ref(0);
const offsetX = ref(0);
const dragging = ref(false);
const startX = ref(0);
function getPuzzlePiecePath(size) {
const s = size;
return `
M${10*2} 0
h${s / 3 - 10*2}
a${10*2} ${10*2} 0 0 1 0 ${20*2}
h${s / 3}
a${10*2} ${10*2} 0 0 0 0 -${20*2}
h${s / 3 - 10*2}
v${s / 3 - 10*2}
a${10*2} ${10*2} 0 0 1 -${20*2} 0
v${s / 3}
a${10*2} ${10*2} 0 0 0 ${20*2} 0
v${s / 3 - 10*2}
h-${s / 3 - 10*2}
a${10*2} ${10*2} 0 0 1 0 -${20*2}
h-${s / 3}
a${10*2} ${10*2} 0 0 0 0 ${20*2}
h-${s / 3 - 10*2}
z
`;
}
const clipPath = `path('${getPuzzlePiecePath(pieceSize)}')`;
function init() {
uni.createSelectorQuery()
.in(container.value)
.select('.bg-image')
.boundingClientRect(data => {
if (!data) {
console.error('无法获取.bg-image尺寸');
return;
}
console.log('图片宽高:', data.width, data.height); // 加这个调试!
containerWidth.value = data.width * 2;
containerHeight.value = data.height * 2;
originX.value = Math.random() * (containerWidth.value - pieceSize * 2) + pieceSize;
// console.log("!!!!!",originX.value)
if (originX.value < 100) {
originX.value = 100;
}
originY.value = containerHeight.value / 2;
offsetX.value = 0;
console.log('originX:', originX.value, 'originY:', originY.value);
})
.exec();
}
function onStart(e) {
dragging.value = true;
startX.value = e.touches ? e.touches[0].clientX * 2 : e.clientX * 2;
window.addEventListener('mousemove', onMove);
window.addEventListener('mouseup', onEnd);
window.addEventListener('touchmove', onMove);
window.addEventListener('touchend', onEnd);
}
function onMove(e) {
if (!dragging.value) return;
const clientX = e.touches ? e.touches[0].clientX * 2 : e.clientX * 2;
let dx = clientX - startX.value;
dx = Math.max(0, Math.min(dx, containerWidth.value - pieceSize));
offsetX.value = dx;
}
const uToast = ref(null)
function onEnd() {
dragging.value = false;
window.removeEventListener('mousemove', onMove);
window.removeEventListener('mouseup', onEnd);
window.removeEventListener('touchmove', onMove);
window.removeEventListener('touchend', onEnd);
if (Math.abs(offsetX.value - originX.value) < tolerance) {
// console.log('验证成功');
uni.showToast({
title: '验证成功',
icon: 'none', // 不显示图标(提示信息)
duration: 2000 // 显示时长(毫秒)
})
emit('success')
} else {
offsetX.value = 0;
uni.showToast({
title: '验证失败',
icon: 'none', // 不显示图标(提示信息)
duration: 2000 // 显示时长(毫秒)
})
}
}
const bgImage = ref("");
onLoad(() => {
let randomInt = Math.floor(Math.random() * 4);
const bgImageMap = [img0, img1, img2, img3];
bgImage.value = bgImageMap[randomInt];
})
onBeforeUnmount(() => {
window.removeEventListener('mousemove', onMove);
window.removeEventListener('mouseup', onEnd);
window.removeEventListener('touchmove', onMove);
window.removeEventListener('touchend', onEnd);
});
</script>
<style scoped lang="scss">
.captcha-container {
/* margin: 20rpx auto; */
user-select: none;
background-color: #fff;
width: 600rpx;
height: 700rpx;
margin: 0 auto;
z-index: 999;
border-radius: 30rpx;
overflow: hidden;
padding: 0 30rpx;
}
.captcha-image {
position: relative;
overflow: hidden;
}
.bg-image {
position: relative;
z-index: 1;
display: block;
/* margin-top: 30rpx; */
width: 100%;
height: 100%;
}
.overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 100;
pointer-events: none;
}
.hole,
.piece {
position: absolute;
z-index: 101;
/* border-radius: 5rpx; */
box-shadow: 0 0 6rpx rgba(0, 0, 0, 0.4);
}
.hole {
background-color: rgba(0, 0, 0, 0.3);
}
.piece {
background-repeat: no-repeat;
background-size: cover;
cursor: grab;
}
.slider-bar {
position: relative;
height: 100rpx;
margin-top: 0rpx;
background: rgb(245, 246, 252);
/* border-radius: 80rpx; */
width: 540rpx;
display: flex;
justify-content: center;
align-items: center;
.slider-bar-font {
// z-index: -1;
color: rgb(169, 169, 171);
font-size: 28rpx;
}
}
.slider-button {
position: absolute;
top: 0rpx;
left: 0;
width: 100rpx;
height: 100rpx;
background: #fff;
/* border-radius: 60rpx; */
box-shadow: 0 0 20rpx rgba(0, 0, 0, 0.2);
transition: background 0.3s;
user-select: none;
border-radius: 10rpx;
border: 3rpx solid rgb(139, 218, 202);
display: flex;
justify-content: center;
align-items: center;
}
.slider-button.success {
background: #4caf50;
}
.font-title {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
height: 100rpx;
font-size: 35rpx;
font-weight: 700;
}
</style>