当前位置: 首页 > Web前端 > vue.js

手机日历组件的设计与实现

时间:2023-04-01 00:55:16 vue.js

前言在大多数客户端应用中,日期的选择和操作是一个常见的功能,使用日历组件来实现这一功能往往是一种高效的解决方案。对于日历组件的设计开发,在常见的开源项目中,通常有两种设计思路:横向切换显示,默认渲染单个月份,通过按键或左右滑动切换月份;垂直切换显示,默认呈现多个月份。月份,上下滑动切换月份;比如添加picker切换视图,添加自定义按钮,日期单选/多选,自定义文案,日期范围限制等功能,这些基本都是基于两个idea的功能扩展。在日常应用中,两种方式各有优缺点:横向切换,初始渲染节点较少,渲染性能较好;垂直切换,具有更直观的视觉体验和更好的交互操作;然而,鱼与熊掌不可兼得,交互体验与性能之间的权衡是必须始终直面的问题。随着移动设备的不断发展和移动浏览器的不断完善,用户设备的兼容性和运行效率得到了显着提高。因此本文主要介绍垂直切换实现的NutUICalendar日历组件。题目介绍今天的题目是NutUI日历组件的设计与实现。Calendar组件是NutUI的一个日历组件。用于为用户提供直观的日期选择方式,滑动切换月份,支持选择单个日期和日期范围,支持自定义日期内容等功能。今天,我们就来看看在组件开发过程中,如何一步步实现组件功能。组件设计思路对于日历组件来说,无论交互如何设计,日期时间数据的处理都是必不可少的。毕竟视图也是服务于数据信息的。本文采用的垂直切换显示方式,也意味着我们需要对节点的渲染性能做一些优化调整。因此,我们的实现思路主要有以下几点:日期数据处理,原始数据一次性初始化,可见区域节点元素分段渲染。应用虚拟列表的方法来降低节点元素的渲染成本。完善滚动事件和边界条件的处理功能,丰富Slots、Props、Events事件等,完善可扩展组件的实现原理。基本参数要求在处理日期数据时,我们需要首先指定我们需要的基本时间输入参数,例如:日历组件可选的时间范围,当前选择的时间。通过对传入参数的分析处理,得到我们需要的数据内容,并在后续的开发过程中,完成组件内容的渲染和事件处理。这里我画了一张图方便大家更好的理解:原始日期数据:就是我们根据日期范围计算出来的原始数据当前选择日期:显示当前月份在可见范围内,需要判断选择的日期是否在范围内daterange显示范围区间:根据当前选择的日期处理,是当前需要渲染的数据范围。容器大小信息:用于计算日期滚动和切换时的位移信息。日期数据处理日期数据的计算需要多个处理过程。首先,我们需要计算传入日期范围是否存在。如果不是,则默认使用最近一年的时间范围。然后计算存在多少个月。根据月数遍历生成日期数据。在计算单个月的日期时,每个月的第一天和一周的最后一天是不同的。我们需要根据不同的周数完成上个月和下个月的日期。这样既可以省去1号起始位置偏移量的计算,又可以为功能扩展做铺垫。//获取单个月份的日期和状态constgetDaysStatus=(currMonthDays:number,dateInfo:any)=>{let{year,month}=dateInfo;返回Array.from(Array(currMonthDays),(v,k)=>{return{day:k+1,type:"curr",year,month,};});//获取上个月最后一周的天数,填上当月的空白constgetPreDaysStatus=(preCurrMonthDays:numberweekNum:number,dateInfo:any,)=>{let{year,month}=日期信息;如果(周数>=7){周数-=7;}让months=Array.from(Array(preCurrMonthDays),(v,k)=>{return{day:k+1,type:"prev",year,month,};});返回months.slice(preCurrMonthDays-weekNum);};};处理后的数据如下:virtuallist当我们生成或加载数据量非常大时,会出现严重的性能问题,导致视图在一段时间内变得对操作无响应。小程序中视图渲染的问题更加明显。为了解决这个问题,虚拟列表是一个很好的解决方案:相对于全量渲染数据生成的视图,可以只渲染当前可见区域(visibleviewport)的视图。当用户滚动到可见区域时,将呈现不可见区域中的视图。比如Taro中的长列表渲染(虚拟列表):当然,以上只是一个简单的应用,日历组件的构建需要在此基础上进行优化。如下图所示,monthswrapper是需要显示月份的容器。这是因为在我们的视口中会有超过一个月的时间。同时,由于单个月份包含的节点较多,经过视口边界后渲染时可能会出现空白区域,因此我们可以保留月份的部分内容,在不可见区域更改渲染节点。如上图,scrollWrapper:是一个高度为总月高度的容器,主要用作viewport中的滚动容器;monthsWrapper:里面是当前渲染月份的容器;viewport:是当前的视口范围;当scrolling事件被触发时,scrollWrapper会向下或向上移动。到达边界后,monthsWrapper内部的月份信息发生变化,其整体高度也可能发生变化。通过修改monthsWrapper的transition,保证月份改变后,视口内的内容不变,更新视口外的数据。在应用虚拟列表的同时,结合目前主流的框架,将数据添加到框架的响应式数据中。该框架根据不同的数据使用diff算法或其他机制在一定程度上复用DOM节点,减少DOM节点的数量。元素的添加和删除操作。毕竟频繁增删DOM是一件比较耗性能的事情。{{month.title}}{{day.type=='curr'?day.day:''}}今天{{startText}}{{endText}}事件处理和边界状态事件选择Calendar组件中月份的切换和变化通过考虑实现滚动事件监听使用滚动事件是考虑到太郎转化为微信小程序的兼容处理,touchmove事件也可以实现加载和切换交互,但是touch事件需要频繁触发事件来修改元素为了实现滚动效果Location,在小程序中,表现为频繁的setData,这会造成较大的性能开销,导致页面卡顿。边界条件确定后,边界条件的判断是一个需要考虑的问题:每个月份所占的高度不是必需的大致相同。每个月包含几个星期,这些星期不一定相同。这样一来,每个月所占据的高度就不一定相同了。因此,要准确判断当前滚动位置信息,需要找一个使用相同的点来判断。这里我们以单日身高作为参考值,通过单日身高计算月份的身高,进而得到单月的平均身高。将滚动位置除以平均高度以获得近似电流。如下图:在计算高度的过程中,因为小程序的单位是rpx,h5是rem,所以需要转换px。lettitleHeight,itemHeight;//计算单个日期的高度//转换为小程序和H5的px处理,rpx和remif(TARO_ENV==="h5"){titleHeight=46*scalePx.value+16*scalePx.值*2;itemHeight=128*scalePx.value;}else{titleHeight=Math.floor(46*scalePx.value)+Math.floor(16*scalePx.value)*2;itemHeight=Math.floor(128*scalePx.value);}monthInfo.cssHeight=titleHeight+(monthInfo.monthData.length>35?itemHeight*6:itemHeight*5);letcssScrollHeight=0;//保存月份位置信息if(state.monthsData.length>0){cssScrollHeight=state.monthsData[state.monthsData.length-1].cssScrollHeight+state.monthsData[state.monthsData.length-1].cssHeight;}monthInfo.cssScrollHeight=cssScrollHeight;当我们得到当前的平均电流后,我们就可以进行边界条件的判断了。constmothsViewScroll=(e:any)=>{constcurrentScrollTop=e.target.scrollTop;//获取平均currentletcurrent=Math.floor(currentScrollTop/state.avgHeight);if(current==0){if(currentScrollTop>=state.monthsData[current+1].cssScrollHeight){current+=1;}}elseif(current>0&¤t=state.monthsData[current+1].cssScrollHeight){current+=1;}if(currentScrollTop=state.monthsData[current+1].cssScrollHeight+state.monthsData[current+1].cssHeight){current+=1;}if(currentScrollTop结束语本文介绍了NutUI中日历组件的设计思路和实现原理,希望能给大家一些启发和思路。最后要提一下我们的NutUI组件库。长期以来,团队成员一直在努力维护NutUI。在以后的日子里,这份坚持不会放弃。我们仍然会积极维护和迭代,为有需要的同学提供技术支持,不定期发布一些相关的文章,帮助大家更好的了解和使用我们的组件库。快点个Star??支持我们吧~