Taro小程序图片滑动验证
环境:taro v3.6.8
,taro-ui 3.1.0-beta.5
;
简介
组件适用于以base64为图片格式的滑动验证方式(示例以后台返回图片大小300*150px为基准)。
关闭验证码逻辑没有写进去只有一个测试性的操作入口,按照个人逻辑进行改造和调整样式参数即可。
组件逻辑:从后端获取背景图、拼图(残缺部分)、拼图据背景图上方的距离以及其他验证所需的参数,父组件调用子组件渲染。子组件内部也可调用父组件刷新逻辑对相关数据进行更改,用户操作完成后在父组件中验证操作,主要验证移动的X距离。父组件与后端交互判断,成功后关闭,不成功刷新子组件再操作。
目录结构
|-- root
|-- src
|-- |-- components
| |-- ImageCode
| |-- index.jsx
| |-- index.scss
|-- |-- page
|-- hello
|-- index.config.js
|-- index.jsx
|-- index.scss
图片滑块验证组件
components/ImageCode/index.jsx
import { View, Image, MovableView, MovableArea } from '@tarojs/components';
import './index.scss'
import { useState } from 'react'
import { useEffect } from 'react';
const ImageCode = (props) => {
const {
imageTop,
pin,
bak,
onRefresh,
onCheck,
onClose,
} = props;
// MovableView为非受控组件,需要记录数值作为初始化时改变的参数
const [sliderX, setSliderX] = useState(0);
const [sliderTemp, setSliderTemp] = useState(0);
useEffect(() => {
setSliderTemp(0);
setSliderX(0);
}, [imageTop])
const flushCode = () => {
onRefresh();
setSliderTemp(0);
setSliderX(0);
}
const onCloseView = () => {
onClose();
}
return (
<View className='imageCodeBox'>
<View className='headBox'>
<View>请完成安全验证</View>
<View className='actionBox'>
<View className='flushBox' onClick={() => flushCode()}>刷新</View>
<View onClick={() => onCloseView()}>X</View>
</View>
</View>
<View className='imageContentBox'>
<Image className='imageContent' src={bak} />
<View className='smegma'></View>
</View>
<View className='dividerBox'></View>
<MovableArea className='moveArea'>
<MovableView
className='moveView'
onTouchEnd={(e) => {
setSliderX(sliderTemp);
onCheck(sliderTemp); // 送检
}}
inertia={false}
direction='horizontal'
onChange={(e) => setSliderTemp(e.detail.x)}
friction={4}
x={sliderX}
>
<View className='puzzleBox' style={{ marginTop: ${imageTop}px }}>
<Image className='puzzle' src={pin} />
</View>
<View className='sliderBox'>
<View className='slider'>→</View>
</View>
</MovableView>
</MovableArea>
</View>
)
}
export default ImageCode;
components/ImageCode/index.scss
// 单位使用大写PX可以强制使用px,不参与变化
.imageCodeBox {
width: 100%;
padding: 10PX calc(50% - 150PX);
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-between;
.headBox {
display: flex;
justify-content: space-between;
width: 100%;
height: 30PX;
.actionBox {
display: flex;
justify-content: space-between;
width: 50PX;
.flushBox {
margin-right: 6PX;
}
}
}
.imageContentBox {
width: 300PX;
height: 150PX;
position: relative;
.imageContent {
display: inline-block;
width: 100%;
height: 100%;
}
.smegma {
position: absolute;
top: 0;
width: 100%;
height: 100%;
z-index: 10002;
}
}
.dividerBox {
width: 100%;
height: 10PX;
z-index: 10002;
}
.moveArea {
width: 100%;
height: 30PX;
background-color: #F7F7F7;
position: relative;
border: 0.5PX solid #eee;
box-sizing: border-box;
.moveView {
width: 50PX;
height: 190PX;
position: absolute;
bottom: 0;
top: -160PX;
z-index: 9999;
border-radius: 6PX;
color: #fff;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
.puzzleBox {
width: 100%;
height: 50PX;
.puzzle {
display: inline-block;
width: 100%;
height: 50PX;
}
}
.sliderBox {
width: 100%;
height: 30PX;
z-index: 10000;
padding: 0 5PX;
box-sizing: border-box;
.slider {
width: 100%;
height: 30PX;
border-radius: 4PX;
background-color: #fff;
z-index: 10001;
color: #000;
text-align: center;
line-height: 28PX;
box-sizing: border-box;
border: 1PX solid #eee;
}
}
}
}
}
父组件调用
pages/hello/index.jsx
import { View, Image, MovableView, MovableArea } from '@tarojs/components'
import './index.scss'
import { AtButton } from 'taro-ui'
import { useState, useEffect } from 'react'
import ImageCode from '../../components/ImageCode'
export default function Hello() {
const [imageTop, setImageTop] = useState(0); // 拼图与背景图相对定位的top值
const [pin, setPin] = useState("");
const [bak, setBak] = useState("");
useEffect(() => {
// 网络初始获取验证码图片数据
... // 网络请求
setPin("");
setBak("");
setImageTop(19);
}, [])
const resetCode = () => {
// 网络请求获取新的验证码相关参数
... // 网络请求
setPin("");
setBak("");
setImageTop(29);
}
const checkCode = (num) => {
console.log(num);
// 做校验,成功关闭验证框,失败刷新
// closeCodeView();
// resetCode();
}
const closeCodeView = () => {
console.log("C-C-V");
}
return (
<View className='hello'>
<ImageCode imageTop={imageTop} pin={pin} bak={bak} onRefresh={() => resetCode()} onCheck={(num) => checkCode(num)} onClose={() => closeCodeView()}></ImageCode>
<AtButton onClick={() => resetCode()}>复位</AtButton> // 实验性复位
</View >
)
}