原生js+css+html實現時序圖

前言

前面我用React + AntDesign 實現了一個簡單的時序圖,可是後來有了更復雜的需求,而且要求同時展現2000個任務的展現,這就涉及到了性能問題,本人先使用React+antd+ts實現了一個基本知足下面需求的demo,可是react的渲染機制形成了較大的性能問題,利用chrome自帶的Performance,測試發現demo的首次渲染高達10s以上,而且後續的操做也會使整個頁面很是卡。時序圖設計到的摺疊和展開功能涉及到整個頁面的重繪,通過思索後決定使用原生js+css去實現,由於原生js下性能是最優的。javascript

需求

什麼是時序圖

  1. 有一個以時間爲單位的座標軸。
  2. 以任務的耗時時長計算任務在頁面上的顯示長度。
  3. 用線條鏈接的方式表示任務之間的關係,可追蹤子任務,父任務。例如:從何時建立,從何時開始。
  4. 每一個任務裏附帶有任務的信息。例如:任務的id,任務的名字,任務的狀態。
  5. 用不一樣顏色區分任務的運行狀態。

功能點

下面先來講說新版本的需求:css

  1. 左側以樹形結構展現任務,可摺疊
  2. 右側展現任務運行所耗時間的長度
  3. 須要用線條連接任務之間的關係
  4. 右側可縮放查看詳細的任務狀態
  5. 縮放時圖形保持以鼠標爲中心向兩端成必定比例放大,放大時裏面的文字描述不受影響
  6. 圖形縮放時表示任務耗時的時間以及座標須要跟隨圖像放大的比例進行相應變化
  7. 鼠標在時序圖上移動時出現一根線條提示當前的時間以及信息

效果圖

圖一:html

圖二:前端

實現

如何實現鼠標滾輪縮放?

鼠標縮放產生時序圖X軸的縮放。時序圖的縮放,在這裏提供三種思路:html5

  1. 作數據截取,按照必定的算法截取先後的數據,而後從新渲染整個頁面
  2. 利用css3的scaleX對時序圖的dom作縮放
  3. 實際改變時序圖dom的width,裏面的任務運行的長度,鏈接線條的長度,任務運行預計須要的時長都以百分比顯示。

三種思路的優缺點:java

  1. 優勢:不須要去操做dom的css屬性,之間從新渲染,比較方便。缺點:對於使用dom重繪,耗費性能嚴重,大量任務渲染時性能很慢。
  2. 優勢:只需改變dom的css,加載快,較流程。缺點:計算麻煩,使用過scaleX的小夥伴會發現當我X軸放大時垂直鏈接線會變寬,字體會橫向拉伸,都須要去反向縮小。
  3. 優勢:加載快,很流暢,一次計算好元素所佔寬度的佔百分比,後面的操做都不須要去計算。缺點:使用百分比計算會有必定偏差,放大到必定程度會看的出來。(綜合考慮,本人採用的是第三種)
// 計算寬度百分比的函數
// endTime: 任務的結束時間
// startTime: 任務的開始時間
// maxTime: 全部任務結束時間最大的值
// minTime: 全部任務開始時間最小的值
// time: 全部任務開始時間與結束時間的排序 升序
// task_width: 任務的長度、水平鏈接線的長度、垂直鏈接線的left值
const widthFun = function (endTime, startTime, maxTime, minTime) {
    const task_width =
    (((Number (endTime) - Number (startTime)) /
    ((maxTime || time[time.length - 1]) - (minTime || time[0])) *
    (body_width - tree_box_dom.offsetWidth)) / dom.offsetWidth)*100;
    return task_width> 100 ? 100 : task_width;
  };
複製代碼

如何在縮放時保持以鼠標爲中心?

先放推理過程圖:react

// 上圖解釋
// dom = 時序圖的dom元素
// domL1, domeL2 = dom.scrollLeft;
// domeL1表示前一次的dom.scrollLeft;
// domeL2表示當前的dom.scrollLeft;
// scale 表示當前的放大的比例
// scale1 表示上一次的放大比例
// tree_dom.offsetWidth表示左側樹的寬度
// clientX1 表示上一次的鼠標位置距離時序圖左側的距離 = e.clientX - tree_dom.offsetWidth
// clientX2 表示當前鼠標位置距離時序圖的距離

<!-- 當鼠標縮放時,獲得公式 -->
domL2 = domeL1(scale/scale1) + clientX1(scale/scale1) - e.clientX + tree_dom.offsetWidth
// 公式講解:
// 1. scale/scale1表示本次的縮放比例除以上一次的縮放比例,表示當前的縮放比例
// 左側捲去的寬度在第二次縮放時也會跟着縮放,因此左側的寬度須要乘以縮放比例
// 鼠標位置距離時序圖左側的寬度在縮放時也會跟着縮放,因此也要乘以縮放比例
// 最後面減去鼠標位置距離時序圖左側的實際距離就等於縮放時左側捲去的長度

// 頁面代碼
time_box_parent.scrollLeft = (time_box_parent.scrollLeft + e.clientX - tree_box_dom.offsetWidth) * (scale_x / scale_x1) - e.clientX + tree_box_dom.offsetWidth;

複製代碼

如何高效的繪製任務連線?

方案:css3

  1. 採用的時css3 + js + html5,用僞元素繪製。
  2. 用dom包裹直角圖片,設置其位置及高度。
  3. 用標籤繪製

優缺點:git

  1. 優勢:不會增長多餘的標籤,對渲染有利。缺點:父任務產生了多個子任務,很差添加僞類及設置僞類的高度及寬度。
  2. 優勢:方便,只要計算子任務距父任務的高度便可。缺點:任務過多時圖片會很是多,很影響性能
  3. 優勢:單獨控制每一個元素的高度及位置,可控性高,可添加反饋色。缺點:添加了較多的元素,對渲染產生影響(本人使用的是第三種,這是一個笨方法,有更好方法的大佬,能夠提供建議,多謝)

實現思路:github

用一個變量記錄每一個任務的層級深度,層級深都以當前任務的父任務爲起點,就是說是從哪一個任務產生的當前任務,同級的子任務進行累加操做。用累加的變量按照必定的比例獲取垂直連線的高度以及水平連線的top值,水平連線的長度由任務的建立時間和開始時間決定。(使用上面的寬度百分比函數)

如何在縮放時調整橫軸單位

這個比較簡單,實現思路:

由於本demo的時間4刻度是個刻度,判斷最小時間戳與最大時間戳之間的差除以4,是否還有一天的時間(60 * 60 * 24,換算成秒),從大到小的降序獲取時間單位。

總結

使用原生js+html+css比使用框架渲染大量demo性能更好。完成了一樣的效果與功能,使用chrome工具測試,渲染時間直用了1.3s左右,相較於以前的性能提升了10倍以上。固然框架有框架的優點,React中,render執行後得到的並非真正的DOM節點,而是一個虛擬的Virtual DOM (JavaScript對象),虛擬DOM具備batching(批處理)和高效的Diff算法,虛擬DOM與真實DOM造成對比,當虛擬DOM發生變化時,把變化的部分同步到真實的DOM上面去,造成局部更新。假如咱們2000個元素分紅20個邏輯塊,每一個邏輯塊包含50個元素,這時若是咱們採用React的話就只須要更新對應的邏輯塊,在這種狀況下React的Virtual DOM的渲染效率就會更高。由於咱們的時序圖是整個頁面的刷新,大量DOM的重繪,因此用原生效率更高。最後在多說一句:框架要會用,原生是根本。

思路指導:yalishizhude

參考資料:

React虛擬DOM淺析

OpenTracing

原文連接:tech.gtxlab.com/timingDiagr…

做者信息:寧文飛,人和將來大數據前端工程師

相關文章
相關標籤/搜索