officialAccount/compontent/public/huakuai.vue

270 lines
6.1 KiB
Vue

<template>
<view class="captcha-container" id="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 + '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 + 'rpx',
width: pieceSize + 'rpx',
height: pieceSize + 'rpx',
backgroundImage: `url(${bgImage})`,
backgroundSize: containerWidth + 'rpx ' + containerHeight + 'rpx',
backgroundPosition: `-${originX+30}rpx -${originY+43}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="onStart"
@touchmove="onMove"
@touchend="onEnd"
:style="{ left: offsetX + 'rpx', maxWidth: (containerWidth - pieceSize) + 'rpx' }"
>
<image
src="https://www.focusnu.com/media/directive/login/right.png"
style="width: 50rpx; height: 50rpx;"
mode="widthFix"
/>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted, nextTick, getCurrentInstance } from 'vue'
const emit = defineEmits(['success'])
const pieceSizePx = 50
const pieceSize = pieceSizePx * 2
const tolerance = 20
const containerWidth = ref(400)
const containerHeight = ref(400)
const originX = ref(0)
const originY = ref(0)
const offsetX = ref(0)
const dragging = ref(false)
const startX = ref(0)
const bgImage = ref('')
const instance = getCurrentInstance()
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() {
nextTick(() => {
if (!instance) {
console.error('无法获取组件实例')
return
}
uni.createSelectorQuery()
.in(instance.proxy)
.select('.bg-image')
.boundingClientRect(data => {
if (!data) {
console.error('无法获取图片尺寸')
return
}
containerWidth.value = data.width * 2
containerHeight.value = data.height * 2
originX.value = Math.random() * (containerWidth.value - pieceSize * 2) + pieceSize
if (originX.value < 100) originX.value = 100
if (originX.value > 400) originX.value = 400
originY.value = containerHeight.value / 2
offsetX.value = 0
})
.exec()
})
}
function onStart(e) {
dragging.value = true
startX.value = e.touches[0].clientX * 2
}
function onMove(e) {
if (!dragging.value) return
const clientX = e.touches[0].clientX * 2
let dx = clientX - startX.value
dx = Math.max(0, Math.min(dx, containerWidth.value - pieceSize))
offsetX.value = dx
}
function onEnd() {
dragging.value = false
if (Math.abs(offsetX.value - originX.value) < tolerance) {
uni.showToast({
title: '验证成功',
icon: 'none',
duration: 2000
})
// console.log("????", originX.value)
emit('success')
} else {
offsetX.value = 0
uni.showToast({
title: '验证失败',
icon: 'none',
duration: 2000
})
}
}
onMounted(() => {
const images = [
'https://www.focusnu.com/media/directive/login/0.png',
'https://www.focusnu.com/media/directive/login/1.png',
'https://www.focusnu.com/media/directive/login/2.png',
// 'https://www.focusnu.com/media/directive/login/3.png'
]
bgImage.value = images[Math.floor(Math.random() * images.length)]
console.log('加载图片:', bgImage.value)
})
</script>
<style scoped lang="scss">
.captcha-container {
user-select: none;
background-color: #fff;
width: 600rpx;
height: 700rpx;
margin: 0 auto;
border-radius: 30rpx;
overflow: hidden;
padding: 0 30rpx;
}
.captcha-image {
position: relative;
overflow: hidden;
}
.bg-image {
position: relative;
z-index: 1;
display: block;
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;
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);
width: 540rpx;
display: flex;
justify-content: center;
align-items: center;
.slider-bar-font {
color: rgb(169, 169, 171);
font-size: 28rpx;
}
}
.slider-button {
position: absolute;
top: 0rpx;
left: 0;
width: 100rpx;
height: 100rpx;
background: #fff;
box-shadow: 0 0 20rpx rgba(0, 0, 0, 0.2);
user-select: none;
border-radius: 10rpx;
border: 3rpx solid rgb(139, 218, 202);
display: flex;
justify-content: center;
align-items: center;
}
.font-title {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
height: 100rpx;
font-size: 35rpx;
font-weight: 700;
}
</style>