實作矩形圖排序動畫|網頁前端

Doppler Kuo
5 min readApr 26, 2019

--

(1) 這篇雖然用 React 實作,但核心概念其實跟 React 無關
(2) 這只是其中一種方法紀錄,歡迎大家來討論更多實作方式
(3) 先不討論套其他套件,只單純紀錄我的實作方法 !

矩形圖表(Bar Chart)很常出現在產品中,有時候會被要求是由數據多至少排序;會實作到此動畫,原因除 PM 想要在使用者切換不同單位時「可以咻咻咻」之外,最大意義提示使用者,

「知道排序在發生且知其如何變化」

例如原來:

添加了動畫後:

可較明顯知道誰正在往上或往下,讓使用者注意到其變化

動畫實作 (使用 React, ES6 語法當範例)

「用添加排序鍵值」來取代「改變陣列的順序」

(Before) 原本做法:改變陣列順序

直接改變原資料順序,然後拿經排序過後的資料去 render 於畫面上,假設今天有一包資料 (Array of object) 如下

const adData = [
{ name: "Eva", impressions: 2953, clicks: 993, ctr: 0.33 },
{ name: "Tina", impressions: 1933, clicks: 492, ctr: 0.25 },
{ name: "Doppler", impressions: 1230, clicks: 390, ctr: 0.31 },
]

原本圖表中 Y 軸的物件以 impressions 做排序,在切換想看 clicks 時,改用 clicks 做排序,此時我們可能會寫

const copyData = [...adData]; // 原生 sort 會改變原陣列,故拷貝
copyData.sort((a, b) => { b.clicks - a.clicks });
return copyData;

最後 render 大功告成!就能做出簡單的排序

(After) 動畫做法:添增每個物件排序的鍵值

如果改變了原來資料的順序,你會發現無論做什麼事都無法讓他們位置有漸變,想要在位置上有漸變必須靠 CSS!!

01. 用 CSS position: absolute + transform: translateY + transition 三個屬性來達到目的吧!

此時你每條 <div class="bar" /> 都可以從乖乖地從上排下來變成:

.bar { 
position: absolute;
transform: translateY(0px); // 其值會用 JavaScript 控制
transition: 0.3s ease; // 動畫要多快及運動方式自由設定
height: 20px;
}

你會發現他擠成一坨且背景高無法撐開,莫急莫慌莫害怕

02. 先添增排序的鍵值給每個物件吧!

const adData = [
{ name: "Eva", impressions: 2953, clicks: 993, ctr: 0.33 },
{ name: "Tina", impressions: 1933, clicks: 492, ctr: 0.25 },
{ name: "Doppler", impressions: 1230, clicks: 390, ctr: 0.31 },
];
const mapOrderIndex = array => {
const copyArray = [...array];
// 先把排序過後的 Name 找出來
const sortedNames = copyArray
.sort((a, b) => b.clicks - a.clicks)
.map(({ name }) => name);
// 再把原來 array 每個物件添增新的 orderIndex key
return array.map(ad => {
return {
...ad,
orderIndex: sortedLabels.indexOf(item.name), // 標記他的排序
}
});
}
const newAdData = mapOrderIndex(adData);

03. 將鍵值與 CSS 屬性連上,大功告成!
此時在 DOM 連結你的 orderIndex 即可完成

<div
className="bar-chart"
style={{ height: newAdData.length * 20 + 'px'}}
>
{
newAdData.map(({ name, clicks, orderIndex }) => {
return (
<div
className="bar-item"
key={name}
style={{
width: (clicks / maxValue) * 100 + '%',
transform: `translateY(${orderIndex * 20}px)`,
}}
>
<div className="label">{name}</div>
<div
className="bar"
style={{
width: (clicks / maxValue) * 100 + '%',
}}
/>
</div>
);
})
}
</div>

最後備註:實際上做法會有兩點不同
a. 會把矩陣裡的物件改成 { label: 'Doppler', value: 100 } 方便切換不同視角時,能維持格式不變,不會像上述例子把 clicks寫死。

b. 上面有個 maxValue 是用來定義最大的數據當分母(所以第一名永遠是 100%)的圖表實作

附上完整範例:

--

--