最近有个需求需要使用原生js实现一个单项选择的picker,类似于
废话不多说,直接上代码:
(其实也没什么好说的,直接看代码清楚点 ......)
// 首先的picker的方法的,事件的封装
import {
useRef,
useEffect,
useCallback,
useState
} from "react";
import { realSize } from "../../utils";
import { useMount } from "ahooks";
const limit = 80;
/** 此处用于记录每个滚动元素的高度; 注意: 你的css高度给多少,此处就是要给多少
我使用了一个适配移动端单位的方法,此处是vw
*/
const realHeight = realSize(30);
const usePickerMethod = (list, selectedValue) => {
const pickerColumsEl = useRef(null);
const startY = useRef(0);
const lastOffset = useRef(0)
const [selected, setSelected] = useState(selectedValue)
// 滚动结束时判断当前选中的值
const slectedColumnsValue = useCallback((top) => {
let currentIndex = -(top / realHeight);
const floor = Math.floor(currentIndex);
if (currentIndex - floor > 0.5) {
currentIndex = floor + 1;
} else {
currentIndex = floor;
}
const sekectedValue = list[currentIndex].label
setSelected(sekectedValue) // 当前选中的值
return { currentIndex }
}, [list]);
useEffect(() => {
const pickerDom = pickerColumsEl.current;
const handleTouchstart = (event) => {
startY.current = event.targetTouches[0].pageY
}
const handleTouchmove = (event) => {
const offset = event.targetTouches[0].pageY - startY.current;
let distance = offset + lastOffset.current;
if (distance > limit) {
distance = limit
}
if (distance < -((list.length - 1) * realHeight + limit)) {
distance = -((list.length - 1) * realHeight + limit)
}
pickerDom.style.transform = `translateY(${distance}px)`
}
const handleTouchend = (event) => {
lastOffset.current += event.changedTouches[0].clientY - startY.current;
if (lastOffset.current > 0) {
lastOffset.current = 0
}
if (lastOffset.current < -((list.length - 1) * realHeight)) {
lastOffset.current = -((list.length - 1) * realHeight)
}
const {currentIndex} = slectedColumnsValue(lastOffset.current);
lastOffset.current = -(realHeight * currentIndex)
pickerDom.style.transform = `translateY(${lastOffset.current}px)`
}
pickerDom.addEventListener('touchstart', handleTouchstart, false)
pickerDom.addEventListener('touchmove', handleTouchmove, false)
pickerDom.addEventListener('touchend', handleTouchend, false)
return () => {
pickerDom.removeEventListener('touchstart', handleTouchstart)
pickerDom.removeEventListener('touchmove', handleTouchmove)
pickerDom.removeEventListener('touchend', handleTouchend)
}
}, [list.length, slectedColumnsValue])
// 定位到页面对应位置
const selectPosition = () => {
list.forEach((item, index) => {
if (item.label === selectedValue) {
console.log(index)
selectIndex(index)
}
})
if (!selectedValue) {
selectIndex(0)
}
}
// 让页面滚动到对应的index位置
const selectIndex = (index) => {
const pickerDom = pickerColumsEl.current;
lastOffset.current = -(realHeight * index);
pickerDom.style.transform = `translate3D(0, ${lastOffset.current}px, 0)`
}
// 仅在首次进入时才执行此方法
useMount(() => {
selectPosition()
})
return { pickerColumsEl, selected }
}
export default usePickerMethod;
其次是页面的html
import React, { useMemo } from "react";
import usePickerMethod from "./hooks";
import './index.css'
const Picker = ({ list, selectedValue }) => {
const { pickerColumsEl, selected } = usePickerMethod(list, selectedValue)
console.log(selected) // 滚动结束后对应的值
const getCols = useMemo(() => {
const result = [];
for (let i = 0; i < list.length; i++) {
result.push(
<div key={i}>
<div className='picker-list'>
<span className=''>{list[i].label}</span>
</div>
</div>
)
}
return result;
}, [list])
return (
<div className='picker-column'>
<div className='up'></div>
<div className='down'></div>
<div className='picker-column-wheel' ref={pickerColumsEl}>
{getCols}
</div>
</div>
)
};
export default Picker;
当让css也很重要
.picker-column {
height: 100%;
user-select: none;
touch-action: none;
position: relative;
overflow: hidden;
}
.up,
.down {
position: absolute;
left: 0;
width: 100%;
z-index: 10;
height: calc(50% - 13px);
pointer-events: none;
}
.up {
top: -5px;
border-bottom: 1px solid #eee;
background: linear-gradient(0deg, hsla(0, 0%, 100%, .7) 50%, #fff);
}
.down {
bottom: -5px;
border-top: 1px solid #eee;
background: linear-gradient(180deg, hsla(0, 0%, 100%, .7) 50%, #fff);
}
.picker-column-wheel{
width: 100%;
cursor: grab;
position: absolute;
top: calc(50% - 15px);
left: 0;
-webkit-transition: all 0.2s ease-out;
transition: all 0.2s ease-out;
}
.picker-list {
font-weight: bold;
font-size: 16px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
}
Picker到这就结束了,还可以添加一些功能,比如类似antd 级联项等。
样式上Column没有做到iOS那种滚轮效果(Column看起来像个圆形的轮子一样)这个css可以后期加上
知道原理了,可以尝试着自己实现日期选择器datepicker。
- 看完如果学会了可以点个赞哦!
- 看完如果学会了可以点个赞哦!
- 看完如果学会了可以点个赞哦! 重要的事情要说三遍!!!