之前本人使用React + AntDesign 實現了一個簡單的時序圖,但是後來有了更複雜的需求,並且要求同時展示2000個任務的展示,這就涉及到了性能問題,本人先使用React+antd+ts實現了一個基本滿足下面需求的demo,但是react的渲染機製造成了較大的性能問題,利用chrome自帶的Performance,測試發現demo的首次渲染高達10s以上,並且後續的操作也會使整個頁面非常卡。經過思索後決定使用原聲js+css+html去實現,因為原聲js下性能是最優的。下面先來說說新版本的需求:
左側以樹形結構展示任務,可折疊
右側展示任務運行所耗時間的長度
需要用線條鏈接任務之間的關係
右側可縮放查看詳細的任務狀態
縮放時圖形保持以鼠標為中心向兩端成一定比例放大,放大時裡面的文字描述不受影響
圖形縮放時表示任務耗時的時間以及坐標需要跟隨圖像放大的比例進行相應變化
鼠標在時序圖上移動時出現一根線條提示當前的時間以及信息
實現難點
鼠標縮放,x軸的縮放方式
鼠標縮放產生時序圖X軸的縮放。時序圖的縮放,在這裡提供三種思路:
做數據截取,按照一定的算法截取前後的數據,然後重新渲染整個頁面
利用css3的scaleX對時序圖的dom做縮放
實際改變時序圖dom的width,裡面的任務運行的長度,連接線條的長度,任務運行預計需要的時長都以百分比顯示。
三種思路的優缺點:
優點:不需要去操作dom的css屬性,之間重新渲染,比較方便。缺點:對於使用dom重繪,耗費性能嚴重,大量任務渲染時性能很慢。
優點:只需改變dom的css,加載快,較流程。缺點:計算麻煩,使用過scaleX的小伙伴會發現當我X軸放大時垂直連接線會變寬,字體會橫向拉伸,都需要去反向縮小。
優點:加載快,很流暢,一次計算好元素所佔寬度的佔百分比,後面的操作都不需要去計算。缺點:使用百分比計算會有一定誤差,放大到一定程度會看的出來。 (綜合考慮,本人採用的是第三種)
// 計算寬度百分比的函數// 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; };
鼠標縮放,保持以鼠標為中心,往兩邊放大
先放推理過程圖:
// 上圖解釋// 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 + js + html5,用偽元素繪製。
用dom包裹直角圖片,設置其位置及高度。
用標籤繪製
優缺點:
優點:不會增加多餘的標籤,對渲染有利。缺點:父任務產生了多個子任務,不好添加偽類及設置偽類的高度及寬度。
優點:方便,只要計算子任務距父任務的高度即可。缺點:任務過多時圖片會非常多,很影響性能
優點:單獨控制每個元素的高度及位置,可控性高,可添加反饋色。缺點:添加了較多的元素,對渲染產生影響(本人使用的是第三種,這是一個笨方法,有更好方法的大佬,可以提供建議,多謝)
實現思路:
用一個變量記錄每個任務的層級深度,層級深都以當前任務的父任務為起點,就是說是從哪個任務產生的當前任務,同級的子任務進行累加操作。用累加的變量按照一定的比例獲取垂直連線的高度以及水平連線的top值,水平連線的長度由任務的創建時間和開始時間決定。 (使用上面的寬度百分比函數)
時間單位:天、時、分、秒
這個比較簡單,實現思路:
因為本demo的時間4刻度是個刻度,判斷最小時間戳與最大時間戳之間的差除以4,是否還有一天的時間(60 * 60 * 24,換算成秒),從大到小的降序獲取時間單位。