我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!
Consic是一个简单易用轻量级React组件库
本文记录了我从零开始如何实现TimePicker组件的结构代码以及功能代码
附上组件源码链接!!!
写在前面
开始接触前端时主要用的都是组件库(element-ui/antd-design),在自己封装组件的过程发现,这也是一件非常有趣的事(或者说是开源的乐趣??哈哈哈)。如果下面的代码中有什么不好的地方希望大家能够帮忙指出!!(我会虚心接受滴)👂
组件展示
开始码代码!
静态结构展示
分析可以看到,组件的时间面板分别由时分秒三列组成,分别的时间范围是
时(00~24)
分(00~60)
秒(00~60)
分别,分别可以上下滑动,有三个特殊的状态分别是激活
悬浮(hover)
禁用
时分秒数组辅助函数
三列采用
div>div*3>span(24|60)
的结构进行生成,时分秒的数字展示其实就是长度为24|60
的的数组下标转z换过来的,因为有index<10
时补零的要求,所以我写了一个小的工具函数来辅助生成对应的数组。代码如下:
const getIdxArr = (length: number) => { const arr = new Array(length).fill(''); return arr.map((_, i) => (i < 10 ? '0' + i : String(i))); };
生成时分秒对应的渲染数组:
const HOUR_LIST = getIdxArr(24); const MIN_AND_SEC_LIST = getIdxArr(60);
主要tsx代码:
<div> {HOUR_LIST.map((_) => ( <span className={`${Number(_) === hour ? 'active' : ''} ${ disableHour !== undefined && disableHour(_) ? 'disable' : '' }`} key={_} > {_} </span> ))} </div> <div> {MIN_AND_SEC_LIST.map((_) => ( <span className={`${Number(_) === min ? 'active' : ''} ${ disableMin !== undefined && disableMin(_) ? 'disable' : '' }`} key={_} > {_} </span> ))} </div> <div> {MIN_AND_SEC_LIST.map((_) => ( <span className={`${Number(_) === second ? 'active' : ''} ${ disableSecond !== undefined && disableSecond(_) ? 'disable' : '' }`} key={_} > {_} </span> ))} </div>
主要的css代码需要实现的功能:
- 让时分秒对应的三个
div
从左往右排以及div中的span
竖着排列div
限定高度出现滚动条- 隐藏滚动条的显示
//此代码已经精简过 display: flex; //限定高度 height: 180px; div { display: flex; flex: 1;//div均分宽度 flex-direction: column;//div竖着排列 max-height: 200px;//设置最大高度,才能显示滚动条(小坑) overflow: auto;//出现滚动功能的核心代码 span { display: flex; margin: 2px; } //以下代码能够隐藏滚动条 &::-webkit-scrollbar { display: none; } } }
功能代码设计
时分秒初始化
const [hour, setHour] = useState(0);
const [min, setMin] = useState(0);
const [second, setSecond] = useState(0);
active类名与数据的绑定
只要需要简单的判断hour|min|second
是否与map
渲染时的的内容Number(_)
相等,相等即为当前选中类名。代码实现:
className={`${Number(_) === hour ? 'active' : ''}`}
disable类名与数据的校验
此处对时分秒的校验需要用户分别传入三个对应的函数,函数默认传入参数是00~24|60
的String
类型参数,需要用户返回boolean
结果,true
禁用false
相反,代码实现:
className={`${disableHour !== undefined && disableHour(_) ? 'disable' : ''}`}
className={`${disableMin !== undefined && disableMin(_) ? 'disable' : ''}`}
className={`${disableSecond !== undefined && disableSecond(_) ? 'disable' : ''}`}
点击时span
元素自动触顶
实现效果
分析
通过查阅资料知道可以通过scrollTo
函数进行实现
Window.scrollTo() 方法
定义和用法
scrollTo()
方法可把内容滚动到指定的坐标。语法
scrollTo(xpos,ypos)
参数 描述 xpos 必需。要在窗口文档显示区左上角显示的文档的 x 坐标。 ypos 必需。要在窗口文档显示区左上角显示的文档的 y 坐标。
思路
将span
元素移动到距离父级div
的顶部,相关抽象代码parentDom.scrollTo(0,childrenDom.offsetTop)
,因此现在要解决的就是如何确定该点击事件是时|分|秒
那个父级div
触发的,以及是div
中哪个span
子元素应该进行移动。
第一个问题我使用了三个useEffect
副作用hook
对hour|min|second
三个state
分别进行监听,在我封装的scrollTo
方法中,传入div
的index
值,通过index
获取到divDom
,再通过选择器函数querySelector
,选中类名为.active
的span
元素。代码如下:
scrollTo()函数
const scrollTo = (eventIdx: number, idx: number): boolean => {
const divDom = document.querySelectorAll('.time-panel div')[idx];
divDom.scrollTo(0, divDom.querySelector(`.active`).offsetTop - 7);
return true;
};
hour|min|second监听hook
useEffect(() => {
scrollTo(hour, 0);
}, [hour]);
useEffect(() => {
scrollTo(min, 1);
}, [min]);
useEffect(() => {
scrollTo(second, 2);
}, [second]);
赋值给Input组件
点击确定按钮,不止需要将选中的值设置到Input
组将中,还需要触发用户可能定义的changeCallback
函数
changeTime函数
函数代码如下:
const changeTime = (newHour: number = hour, newMin: number = min, newSecond: number = second) => {
const time = `${newHour < 10 ? '0' + newHour : String(newHour)}:${
newMin < 10 ? '0' + newMin : String(newMin)
}:${newSecond < 10 ? '0' + newSecond : String(newSecond)}`;
changeCallback && changeCallback(time);
setTimeValue(time);
};
Input组件
<Input
placeholder={placeholder}
defaultValue={timeValue}
type="primary"
showClear={showClear}
clearCallback={handleClear}
/>
终
结语
大一刚接触组件库时,往往都是直接copy
了官网的示例应用到自己的项目中,有时出现了一些与自己想法不符的代码行为,我就会陷入一种很困顿的情境。现在,通过自己封装组件的这一过程,能够加深组件的一些行为的理解。也希望明年步入大三的我能够在暑期中有更多的进步!!(也希望能够找到找到心仪的实习!!!)