為什麼你的時間序列圖表畫到十萬點就卡住
瓶頸幾乎不在圖表套件,而在你丟給瀏覽器的繪製次數。有三個方向可以砍。

接上一週的 tick 資料,圖表一畫,整個頁面就卡死。第一直覺通常是怪圖表套件,然後開始 找「更快的那一個」。這多半是錯的方向——主流套件之間的效能差距大多在兩倍以內,而換套件 會花掉你一整天。
真正的瓶頸
瀏覽器不在乎你有一百萬筆資料,它在乎的是要畫幾個東西。以 SVG 為底的圖表,每一個 資料點就是一個 DOM 節點;一百萬點就是一百萬個節點,版面計算、樣式重算、繪製全部跟著 這個數字放大。換一個更快的套件,救不了這件事。
砍繪製次數的三個方向
- 畫之前先降採樣。 畫布寬 1,600 像素時,能被分辨的點最多就 1,600 個。超過的部分 肉眼看不見,卻照樣吃你的繪製時間。
- 換成 canvas 或 WebGL 渲染。 一次繪製呼叫取代一百萬個節點。代價是失去逐點的 DOM 互動,命中測試要自己實作。
- 在資料源就聚合。 如果資料來自時序資料庫,直接跟它要分桶結果而不是原始 tick。多數 情況下,網路傳輸的成本本來就比繪製還高。
一般人會踩到的地方
最天真的降採樣——每 n 筆取一筆——會剛好抹掉你當初做這張圖是為了看到的那根尖刺。每一百 筆取一筆的價格序列,會安靜地把那根關鍵的 K 棒丟掉。
選著「每 n 筆取一筆」把點數往下拖,看那根尖刺消失。然後不要動滑桿,只切換演算法 ——點數一模一樣,尖刺回來了。
保留 100 / 2000 點尖刺被丟掉了
Largest-Triangle-Three-Buckets 這類演算法就是為此存在的:它保留的是能維持序列視覺 形狀的那些點,而不是剛好落在固定間隔上的點。輸出的點數跟天真取樣一樣,但看起來跟原圖 接近得多。
常見追問
降採樣會不會把真正的極值弄不見?
挑錯演算法才會。「每 n 筆取一筆」會丟掉取樣點之間的所有東西,只有一個 tick 的尖刺就這樣消失了。Largest-Triangle-Three-Buckets 保留的是決定這條線形狀的 那些點,極值包含在內。
瀏覽器實際上畫得動幾個點?
用 SVG,大約超過一萬點就開始出問題——每個點都是一個要排版、要套樣式的 DOM 節點。用 canvas 只有一個元素、一次繪製,幾萬點是很平常的量。
降採樣要在後端做還是瀏覽器做?
讀者不會放大看原始序列的話,在後端做,順便省下大量傳輸。可以放大的話就在 瀏覽器做——每一個縮放層級需要的是從原始資料重新取的一組樣本。