likes
comments
collection

react 日历组件拖拽部分逻辑

作者站长头像
站长
· 阅读数 12

前言

之前写的react组件:

之前写日历组件的上半部分,连接如下: juejin.cn/post/716646…

目标

我们在上半部分实现了一个可以渲染年月的月视图的日历组件。其中有一个api是cellAppend,可以往日历内容区域写内容,如下图

react 日历组件拖拽部分逻辑

代码示例如下:

<Calendar
        cellAppend={(cellData) =>
         判断在某这个日期展示 && (
            <div className="cell-append-demo-outer">
                今天
            </div>
          )
        }
      />

好了,我们这里的目标是

  • 实现渲染一段日期,比如11号到20号,然后这个日期是可以拖拽的。
  • 这段日期的开始位置,可以往左右拉伸,目的让时间段可以增加或者减少
  • 同理日期的末尾也是可以拉伸的
  • 新建日期:在日历每一天的空白处开始拖拽,拖拽出的一片空白区域可以新建日期。

我们只实现基本的情况,因为这里面会涉及不少的判断,比如多个任务重叠,如何重排序的问题。

渲染时间段

这个很简单,我们渲染的数据必然是一个数组,类似:

const eventList = [
  {
    id: 12, // 这段日期的唯一标识id
    startTime: "2018-04-4", // 日期起始
    endTime: "2018-04-15",// 日期结束
    displayName: "日程1",
    userId: 1, // 用户id
  },
  {
    id: 15, // 这段日期的唯一标识id
    startTime: "2018-04-05",
    endTime: "2018-04-06",
    displayName: "日程2",
    userId: 2, // 用户id
  },
];

好了,再根据我们上的cellAppend API来看,我们只需要这么渲染

<Calendar
        cellAppend={(cellData) => {
          eventList.map(event=>{
             计算event.startTime和endTime间隔几天(利用dayjs现成的API)
             然后for循环,遍历这几天 {
                 利用cellData判断是否是for循环的这一天
                 如果是,就渲染出来,渲染的时候需要判断,这是开始,中间还是结束
                 因为UI的展示不一样
             }
             
          })
        }
        }
      />

好了,我们根据上面的代码就可以渲染各种时间段了,注意,因为日历的高度有限,比如有100个任务在同一天,会超出它的高度,一般我看google日历,react-big-calender,outlook日历等等都是会把这种情况列为 还有 xxx任务,比如还有 23个任务,被折叠起来了。

这个被折叠其实我觉得是没办法的办法,就算折叠了,也会出现一些不好处理的情况,比如我有一段日期,是从1号到4号,在第一天要被折叠,因为排的任务太多了,在2-4天不需要折叠,这种情况不太利于一眼就看出来,因为被折叠了,但是看过主流的日历实现,都解决不好这个问题。所以我们如果做的话,最多也只能做到这个程度了,我来再写一下代码。

<Calendar
        cellAppend={(cellData) => {
          eventList.map(event=>{
             计算event.startTime和endTime间隔几天(利用dayjs现成的API)
             然后for循环,遍历这几天 {
                 利用cellData判断是否是for循环的这一天
                 如果是,就渲染出来,渲染的时候需要判断,这是开始,中间还是结束
                + 这里还要判断这个单元格目前的任务数是否超出了,如果超出,就不需要把这个任务展示出来
             }
             
          })
        }
        }
      />

新建日期

对了,我们拖拽这部分使用的是react-dnd这个库,我们会利用这个库,监听每一个日期单元格是否是hover状态,比如说啊,我要新建一个任务,从1号到4号,都是空白日期,我鼠标down,然后down的同时move,相当于就是drag事件,从1号拖鼠标到4号,此时拖拽结束,弹出一个框,让我填写新建任务的名称。

这就是新建日期的交互。

所以我们需要监听drag过程中的hover,其实就是dragover事件。

这里需要注意一个逻辑,比如我从1号,拖拽到4号,此时1到4号会有一个UI变化,比如有个浅蓝色的背景表示正在被新建,我就不能简单写为只要是拖拽到的地方就有浅蓝,有可能到了4号,我又拖到3号,这时候就要取消4号的蓝色背景。

所以我们要换一个思路,因为hover的日期我们知道,那么可以计算hover的日期和拖拽起点的日期的距离,也就是间隔多少天,那么我只需要知道正数是往右多少天的背景变为蓝色,负数就是往左多少天变为蓝色。

然后在dragend事件,重新渲染日历组件,我们上面已经写了渲染日期段的逻辑了,这么就新建了一个日期。

当然我们也可以支持点击某个日期,弹框建立新的日期,点击事件我们上一篇文章的代码已经支持了,这个不多说了。

拖拽日期本身

这就需要我们给所有渲染的日期段加上drag事件,如果用react-dnd大概是这样的


// Cell组件
const [ { }, dragRef ] = useDrag({
    collect: {
        这个cell的数据,比如id,startTime这些
    }
})

<div ref={dragRef}> 日期 </div>

// 日历组件
<Calendar
        cellAppend={(cellData) => {
          eventList.map(event=>{
             return <Cell组件>
             }
             
          })
        }
        }
      />

好了,当拖拽的时候我们实际上就是改变当前这个日期的startTime和endTime,此时重新渲染我们的日历组件即可,当然需要一个状态,表示正在拖拽日期,dragEnd的时候把这个状态重置。

当然这个也是需要dragover事件去监听的,主要就是靠这个拖拽状态变量告诉dragover,此时是在拖拽日期,不是新建日期。

这段日期的开始位置和结束位置的拖拽拉伸

日期比如3号到4号的一个任务,我从3号那里往左拖,意思是把这个任务延长一点,这也是一个比较常见的用户需求。这个咋做呢。

我们其实要建立一个透明的元素,覆盖在日期段的最前面,这样就可以给这个元素添加拖拽事件了。

拖拽逻辑跟上面差不多,就是需要一个新的状态叫拖拽前面比如叫DRAG_FORWARD,在dragover事件上监听一下。

本文结束,欢迎探讨啊,只有业务不帮就更新常见的react组件代码实现思路(排除太简单的组件,比如面包屑这种组件)