前言
一直想做一个滑动验证码的组件。
html 结构
<div className={styles.container}>
<div className={styles.slider}>
<!-- 滑块 -->
<div className={styles.sliderHandle} onMouseDown={mouseDown} style={sliderHandleStyle} > >> </div>
<!-- 轨道 -->
<div className={styles.sliderRail}>
<div className={styles.text}>向右滑动验证</div>
</div>
<!-- 滑块路线 -->
<div className={styles.sliderTrack} style={{width: ((offset.x) / 250 * 100 + "%"), borderRadius: (offset.x < 250) ? 'none' : '45px' }}></div>
</div>
</div>
less 结构
主要是一些定位, 相对定位中的绝对定位
.container {
width: 400px;
height: 400px;
margin: 50px auto;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
user-select: none;
.slider {
width: 250px;
height: 45px;
position: relative;
border-radius: 45px;
.sliderHandle {
width: 45px;
height: 45px;
border-radius: 50%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background-color: #fff;
z-index: 99;
position: absolute;
left: 0;
top: 0;
right: auto;
transform: translateX(-50%);
box-shadow: 1px 5px 2px #eee;
cursor: pointer;
}
.sliderRail {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
border-radius: 45px;
background-color: #f5f5f5;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.sliderTrack {
position: absolute;
width: 0%;
top: 0;
left: 0;
z-index: 89;
right: 0;
bottom: 0;
border-top-left-radius: 45px;
border-bottom-left-radius: 45px ;
background-color: #91d5ff;
}
}
}
代码逻辑
代码中主要使用的是 react Hook, 主要的逻辑是使用了 mouseup, mouseover, mouseDown
const Block: React.FC = () => {
const [dragging, setDragging] = useState(false);
const [origin, setOrigin] = useState({ x: 0, y: 0 });
const [offset, setOffset] = useState({ x: 0, y: 0});
const mouseDown = useCallback(({clientX, clientY}) => {
console.log("mouseDown", clientX);
setOrigin((state) => ({ x: clientX, y: clientY}))
setDragging(true);
}, []);
const mouseMove = useCallback(({clientX, clientY}) => {
let x = clientX - origin.x;
if( x >= 250 ) {
x = 250
} else if(x / 250 * 100 <= 0 ) {
x = 0;
}
const transition = { x, y: clientY - origin.y };
if(x >= 250) {
setDragging(() => false)
}
setOffset(state => {
return {
...state,
...transition
}
});
}, [origin]);
const mouseUp = useCallback(() => {
console.log("up")
setDragging(() => false)
}, []);
useEffect(() => {
if (dragging) {
window.addEventListener('mousemove', mouseMove);
window.addEventListener('mouseup', mouseUp);
} else {
window.removeEventListener('mousemove', mouseMove);
window.removeEventListener('mouseup', mouseUp);
setOffset((state) => {
let x = state.x
if(x>= 250) {
console.log("验证通过")
} else if(x< 250) {
x = 0
}
return {
x: x,
y: state.y
}
})
}
}, [dragging]);
const sliderHandleStyle = useMemo(() => ({
left: (offset.x) / 250 * 100 + "%",
transition: (!dragging ? 'left 500ms' : 'none' )
}), [offset, dragging])
return (
<div>
<div className={styles.container}>
<div className={styles.slider}>
<div className={styles.sliderHandle} onMouseDown={mouseDown} style={sliderHandleStyle} ><DoubleRightOutlined /></div>
<div className={styles.sliderRail}>
<div className={styles.text}>向右滑动验证</div>
</div>
<div className={styles.sliderTrack} style={{width: ((offset.x) / 250 * 100 + "%"), borderRadius: (offset.x < 250) ? 'none' : '45px' }}></div>
</div>
</div>
</div>
);
}
线上仓库
这里记录一下在使用 react hook 的一些问题
先谈谈 useCallback 这个函数吧
useCallback
useCallback 提供了两个参数,一个个回调函数,另一个是依赖数组。
当依赖数组不改变时,此时回调函数不改变,已达到缓存的效果,减少在re-render的时候重新生成函数
案例1
// Child.js
const Child = ({cb}) => {
return (
<div>
<button onClick={cb}>点击</div>
</div>
)
}
const App = () => {
// 此时 当App 重新渲染的时候,会重新生成 memoClick
// const memoClick = () => console.log("click");
// 用了useCallback 后 无论App 是否重新渲染,传给 Child 的 memoClick 都是之前的引用
const memoClick = useCallback(() => console.log("click"), [])
return (
<div>
<Child cb={cb} />
</div>
)
}
其他
当useCallback 的依赖数组 在useCallback内设置变化会导致无限循环
const App = () => {
const [a, setA] = useState(1);
const memoClick = useCallback(() => {
console.log("click")
setA((state) => {
const after = state.a++;
return after
})
}, [a])
return (
<div>
<div>{a}</div>
<Button onClick={memoClick}>点击</Button>
</div>
)
}
然后我找了很多文章,应该只有这篇是讲的比较清晰的, React Hooks(二): useCallback 之痛
后话
验证码组件还没做好,但大体逻辑是这样