如何寫一個拖拽日曆元件(附原始碼)

螞蟻金服資料體驗技術發表於2018-04-03

作者簡介 Kid 螞蟻金服·資料體驗技術團隊

本文會介紹如何寫一個可拖拽日曆元件,偏重點在於日曆元件的功能挖掘以及對於開發過程的一些思考,編碼部分會介紹核心部分的實現。程式碼在最後也會放出來給大家。

效果演示

先看下專案最後的實現效果:

running.gif | left | 663x480

前期調研

要做一個可拖拽日曆元件,先得去看一圈市場上已有的日曆元件做成什麼樣了。主要是為了收集功能以及避免重複造輪子。我調研了google日曆,tower,fullcalendar,teambition,webmail等的日曆控制元件。其中google日曆和fullcalendar算是使用感受比較好的了,以下是他們的使用動圖:

google日曆

google.gif | left | 464x372

fullcalendar

QQ20180402-115350-HD.gif | left | 470x397

功能對比

以下是功能細節的對比列表:

拖拽已有事件
(拖拽預設起始位置)
拖拽已有事件拖拽中效果
(產生遮擋)
左右拖拽事件
(只實現了單方向,並且產生遮擋)
拖拽空白區域新建
事件智慧佈局
(跨周後事件佈局亂了)

從表格的對比可以看到,這幾家實現的還算不錯的日曆元件功能都沒有實現全,而且在一些細節上都沒做好。能找到的開源的只有fullcalendar,不過他是jquery版本的,而且功能實現的並不好,細節上也有不少錯誤,所以就打算自己寫一個了(終於找到重複造輪子的理由了~)。

功能

根據之前的調研,一個較好的可拖拽日曆控制元件需要實現的功能:

  • 拖拽已有事件
  • 雙擊新增事件
  • 左右拖拽事件
  • 拖拽空白區域新建事件

除了功能之外,還有使用者可用性上的細節打磨:

  • 拖拽已有事件的陰影效果
  • 拖拽已有事件拖拽中隱藏
  • 左右拖拽的預覽以及陰影效果
  • 事件智慧佈局,包括堆疊和換行後的冗餘清理
  • 拖拽態的處理(偏移量計算以及dragLayer的重寫)

編碼

需求調研完了進入到編碼過程,專案主要基於react和react-dnd。編碼的整體步驟包括:

  • 繪製日曆
  • 根據事件列表繪製事件
  • 事件佈局整理,堆疊效果和換行清理
  • 讓事件可拖動
  • 讓事件可左右拖動
  • 讓空白區域可拖動勾選
  • 拖拽陰影處理 細講的話文章太長,大家看著累,我講一下關鍵實現點。

可拖拽區域

image.png | left | 463x253

主要是用了react-dnd的拖拽能力,圖裡的4種顏色的圈分別對應了4種可拖拽元件的source。

  • 紫色是事件本身
  • 紅色是向左拖拽區域
  • 黑色是向右拖拽區域
  • 綠色是拖拽空白的區域(這個設計最有意思,用拖拽的行為來模擬選中~)

為這4種source分別處理好對應的拖拽事件。為每個source定義好各自的hover事件可以處理不同的陰影效果。定義好layer可以處理拖拽時滑鼠的預覽效果。

事件智慧佈局

image.png | left | 334x193

事件分類

就是把一天的事件分為如圖所示的5種:

  • 1是有前一天,有後一天
  • 2是無前一天,有後一天
  • 3是有前一天,無後一天
  • 4是無前一天,無後一天
  • 5是空白的填充事件

渲染某一天的時候,先渲染1和3這種有前一天的,因為他們有個穩定的index,然後按照優先順序渲染事件2,然後是4,填不滿空白的渲染事件5。然後將1和2的index傳到下一天。這樣就能較好的佈局了。

新周事件清理

image.png | left | 503x180

如圖,檢測到新一週開始的時候,我會先清理中間的空白。然後再根據優先順序排序事件。

整個開心的程式碼編碼過程結束後,居然發現自己內心有些拒絕這份程式碼..為啥寫完程式碼,除錯完成後,自己不願意再回頭看了呢。哦,是因為真的寫的不好看啊!不好看換句話說就是程式碼的內在質量差。

質量

軟體質量可以分為外在的和內在的。外在質量包括:

  • 可用性
  • 正確性
  • 健壯性
  • ....

外在質量是使用者關心的唯一軟體特性,我上面一直追求和打磨的其實都屬於軟體的外在質量。使用者是爽的~

可我內心有些拒絕這份剛寫的程式碼的原因是因為軟體的內在質量出了問題,內在質量包括:

  • 可讀性(可能是最重要的了)
  • 可維護性
  • 可接入性(現在支援自定義form,感覺還是不錯的~)
  • ....

寫著寫著發現程式碼不好理解了,耐下心細看一下發現:

  • 程式碼有冗餘
  • 子程式命名沒有起好
  • 程式碼結構混亂
  • 瞎定義css名
  • ....

接著幹嘛,先重構咯~

  • 整理程式碼結構,抽抽抽,讓整體流程清晰
  • 重新思考子程式命名,寫註釋
  • 思考對外提供的介面,加強可接入性
  • .....

重構完成一波後思考為什麼內在質量會出現問題呢?

質量上的追求是無窮盡的,無論是內在的還是外在的。很多技術都可以提升質量:補充測試用例,良好的程式設計風格,合理的註釋,分層抽象,合理的介面...讓所有的特性都表現的盡善盡美絕無可能。根據一組互相競爭的目標找出一套解決方案,正是這種情況讓軟體成為一個真正的工程學科

設計

為什麼質量會差呢?當然是因為設計的有問題,才會在開發中發現設計不符合而不斷調整設計,導致破壞了軟體中非常重要的“系統完整性”(人月神話相當推崇的重點)。最終就導致了質量差。

那麼為什麼沒有好好設計呢?我估計是因為沒有抵擋住“趕緊開始編碼”的誘惑! 在思考了如何拖拽已有事件之後我就迫不及待的開始編碼。沒有進行更為詳細的設計。其實這個階段我在大腦裡已經隔絕了後續的需求了(為了智力上可管理採取的自我保護吧~)。而在實現完拖拽已有事件之後,我開始思考如何拖拽空白區域新建,發現已有的設計需要調整。然後在想到了一個還不錯的點子之後就又開始了編碼。設計不完備的迴圈導致了質量的下降。

其實軟體的產生與現實中的食物鏈一樣,需求->設計->編碼。一環套一環,越靠近上游出了問題影響面越大。歸納就是我每次的設計都是基於並不完整的需求來做的。

應該怎麼做呢,應該在最開始的時候儘可能的設計全才行。應該加大在初期投入的設計時間。

迭代式開發

可能有朋友看到這裡感覺,不對啊,設計本來就是“險惡的問題”(也就是必須被解決或者部分解決才能定義的問題)。所以開發過程採用迭代式開發沒問題啊。迭代式的好處在於有利於規避風險。不斷小成本的嘗試來降低失敗的風險。所以本來迭代式的開發就必須配備不斷的重構來保證質量的。

對於這個問題。我覺得應該看專案的規模。看我們腦海中可控的複雜度。可能更好的方式是進行原型的開發進行試驗,試驗發現可能之後立刻重新整體設計,然後再編碼,這樣的成本會更小些。

就是在初次迭代進行了一些難點的驗證之後,進行整體設計,這樣可以減少迭代式開發後必不可少的重構環節,減少整體的開發成本,也提高了專案的質量。

總結

本文分享了可拖拽日曆元件的實現過程,編碼細節以及對於整體開發的思考。之所以會探討對於開發的思考是因為各種方法論都被用於大小不同的專案,對於小專案,方法論的應用顯得很不經意且趨於本能。如果我們有選擇的使用一些方法論的話是能夠減少開發時間以及提升產品質量的。最後附上程式碼的傳送門,具體的接入使用可以看下專案的README,還是有相當多缺陷的,歡迎提PR~

對我們團隊感興趣的可以關注專欄,關注github或者傳送簡歷至'tao.qit####alibaba-inc.com'.replace('####', '@'),歡迎有志之士加入~

原文地址:github.com/ProtoTeam/b…

相關文章