本文作者 字節(jié)跳動-數(shù)據(jù)平臺-melon
引言
在做數(shù)據(jù)可視化相關(guān)的工作中,開發(fā)人員總是必不可免的需要與標簽打打交道。在畫布空間充裕,數(shù)據(jù)量不大胰耗,標簽內(nèi)容簡潔的理想國中,一切自然不成問題芒涡。但是現(xiàn)實場景中柴灯,蕪雜的需求,龐大的數(shù)據(jù)费尽,向我們提出了要求:如何合理的組織標簽的布局赠群,從而盡可能多且美觀的展示標簽。
不同圖表都有著其各自空間以及美學考量的特性旱幼,本文主要著眼于餅圖的標簽布局查描。
首先拋出一個普適的前提:畫布空間是有限的,因此在其中能夠有效展示的標簽數(shù)量是有限制的柏卤《基于這一前提,我們有幾個需要關(guān)注的問題:
如何合理排布標簽闷旧,使得盡可能多的標簽被顯示长豁?
如何在展示較多標簽的情況下,保證標簽的可讀性忙灼,并使得用戶能夠直觀的看出標簽布局的邏輯?
在無法有效容納所有標簽的情況下钝侠,如何挑選更具有價值的標簽信息展示该园?
相關(guān)產(chǎn)品餅圖標簽布局
在討論餅圖布局方案之前,首先讓我們來看看其他產(chǎn)品的餅圖布局:
G2
G2 餅圖標簽會按照餅圖外圓進行排布帅韧,并用短連接線從扇區(qū)指向標簽左端或者右端中點里初。
但是令人費解的是,在頂部以及底部的標簽忽舟,其連接線卻轉(zhuǎn)為了弧線双妨,并指向了標簽上部或者下部,令人很難理解其處理邏輯為何叮阅,甚至于當標簽較密時無法將標簽與連接線相對應刁品。
此外,也能看到其頂部和底部標簽出現(xiàn)了重疊浩姥,導致失去了可讀性挑随。
ECharts
ECharts 對標簽布局提供了幾種方案,自左至右分別是 none勒叠,labelLine兜挨,edge膏孟,其分別意味著按照餅圖外圓排布、標簽水平方向?qū)R拌汇、標簽按畫布邊緣對齊柒桑。三種方案,其標簽位置的 Y 值均相同噪舀。究其本質(zhì)幕垦,后兩種方案其實就是 第一種方案的基礎(chǔ)上改變了標簽的 X 值 而已。
ECharts 餅圖標簽與扇區(qū)的連接線為兩段線傅联,第一段由扇區(qū)指出先改,第二段保證水平。
ECharts 相比較于 G2 而言蒸走,提供了標簽相互躲避的算法仇奶,當標簽數(shù)量非常大時,仍然保證了一定程度的可讀性(后兩種方案)比驻,但是在某些區(qū)域標簽依舊出現(xiàn)了重疊该溯,例如畫布頂部。
第一種方案在標簽數(shù)量較大時表現(xiàn)非常怪異别惦,其標簽布局明顯劃分為了四個區(qū)域狈茉,上下的標簽布局出現(xiàn)了割裂,同時左右標簽互相影響出現(xiàn)了遮擋掸掸,甚至超出了畫布氯庆。
其他
Tableau 不支持引導線的設(shè)置,也就意味著其沒有布局能力扰付。雖然 Tableau 會對遮擋的標簽進行隱藏堤撵,但是其仍然無法支持大量標簽的顯示。
網(wǎng)易有數(shù)也支持標簽的遮擋隱藏羽莺。其同樣采用餅圖外圓的布局实昨,做了標簽的躲避,并且使用了一個較為詭異的貝塞爾連接線來防止連接線穿過餅圖區(qū)域盐固。
其他產(chǎn)品相類似荒给,這里不再贅述。
簡單總結(jié)一下刁卜,為了提升標簽的可讀性志电,許多產(chǎn)品都使用了餅圖外圓弧的布局方式。當標簽數(shù)量較大時长酗,或者隱藏被遮擋標簽僅顯示少量標簽溪北,或者提供標簽的位置調(diào)整并使用引導線連接對應扇區(qū)。
基于我們之前給出的關(guān)注問題,調(diào)整方案能夠容納更多標簽之拨,展示更多信息茉继,比起遮擋而言相對更好。事實上蚀乔,之前在我們自己的產(chǎn)品中也采取了類似 ECharts 的布局方案烁竭,但是由于 ECharts 的布局策略中本身存在諸多不足,因此需要做一次全面的改進吉挣。
布局方案
基于諸多考量派撕,我們提出了自己的標簽布局方案。首先來看看我們的布局效果吧:
最簡單的標簽布局情況自然沒有問題睬魂。
當標簽數(shù)量開始增多的時候同樣能夠合理的組織標簽位置展示终吼。
在數(shù)據(jù)量極其巨大的極端情況下,同樣能夠保證充分利用畫布空間展示有效信息氯哮。
看起來似乎不錯际跪,那么我們做了哪些工作呢?
整體布局策略
一如之前所說喉钢,基于餅圖外圓的標簽排布是許多布局方案的基礎(chǔ)姆打,由于餅圖本身的幾何特性,這一布局同樣也是最為直觀的選擇肠虽。
但是基于外圓的布局存在一個重要的缺陷:圓圖形本身是 **封閉 **的幔戏,也就意味著其所能容納的標簽數(shù)量有限。這一缺陷導致標簽布局無法有效的利用畫布剩余空間税课。這一缺陷當然可以采用擴大外圓半徑的方式來解決闲延,但這也意味著布局同時侵占了畫布橫向與縱向的空間,同時外圓與扇區(qū)中間的間隔過大也影響了布局的美觀伯复。
ECharts 采用了一種并不優(yōu)雅的方式來解決這一問題慨代,即在外圓布局的基礎(chǔ)上引入了第二種布局邏輯。第二種布局邏輯會在標簽數(shù)量較大時生效啸如,其不遵循外圓的約束,而將標簽推移一個固定值氮惯,這也就是導致極端情況下 ECharts none 布局下半?yún)^(qū)域左右標簽過度延伸的原因叮雳。
引入額外的邏輯作為補充導致了兩個惡果:
布局邏輯出現(xiàn)割裂,在局部區(qū)域內(nèi)不統(tǒng)一的邏輯使得用戶感到費解妇汗;
不完善的處理使得某些極端情況下標簽布局結(jié)果不具有可讀性帘不。
雖然 ECharts none 布局在標簽數(shù)量巨大時表現(xiàn)糟糕,但是后兩種布局表現(xiàn)就好上許多杨箭。究其原因寞焙,是源于后兩種標簽在 X 值的計算上有一個統(tǒng)一且可靠的邏輯,即 標簽的 X 值由其 Y 值確定,而不像第一種布局在局部上受到不同計算邏輯影響捣郊。這也給予了我們布局的基礎(chǔ)思路:執(zhí)行遮擋規(guī)避對標簽 Y 值進行調(diào)整辽狈,之后通過全局規(guī)范確定其 X 值。
為此呛牲,我們放寬了外圓布局所暗含的同心圓假設(shè)刮萌,使用兩個圓心不同的圓弧替代外圓布局標簽。在保證標簽 X 值計算具有統(tǒng)一規(guī)范的同時娘扩,支持對標簽數(shù)量進行擴展着茸,如下圖所示:
由于目前標簽布局僅考慮橫排文字,因此將外圓拆分為了左右兩個圓弧琐旁,兩者圓心均與餅圖在同一水平線上涮阔。兩個圓弧與水平線的交點與餅圖圓心的距離相等,即 |A O0| = |B O0|灰殴,該距離由連接線的配置所決定并為一固定值敬特。
確定 |A O0| 以及 O1 在水平線上之后,O1 的位置也就由其半徑所確定验懊。當展示標簽較少時擅羞,外圓能夠容納所有標簽,此時 O1 落在 O0义图,與外圓布局相同减俏。當標簽數(shù)量變大時,標簽在 Y 方向上會執(zhí)行調(diào)整碱工,圓弧半徑也隨之擴大娃承,從而在 Y 方向上占據(jù)更多空間。
介紹了整體的布局策略怕篷,該來談一談一些具體的做法历筝。
遮擋規(guī)避
在計算每個標簽對應扇區(qū)數(shù)據(jù)以及獲取到標簽本身寬高之后,需要對標簽計算其初始的位置廊谓。初始狀況下梳猪,所有標簽在默認情況排布在餅圖外圓的對應位置。
理想狀況下蒸痹,自然是無需做后續(xù)操作春弥,每個標簽呆在其應處的位置。但是當標簽出現(xiàn)遮擋時叠荠,就需要采取一些算法對這一遮擋進行規(guī)避匿沛。
遮擋規(guī)避算法多是大同小異,無非是處理相互遮擋的標簽如何偏移遮擋的距離(我們所采用的規(guī)避算法與 ECharts 相類似)榛鼎。此時我們的指導思想也為我們提供了許多的便利:遮擋規(guī)避僅僅需要考慮標簽的 Y 值逃呼,而 X 值是在后續(xù)過程中被確定的鳖孤。下圖展示了碰撞規(guī)避的簡單流程:
在執(zhí)行布局的過程中,我們會依次向畫布放置標簽抡笼。假設(shè)我們當前所處理的標簽為 A苏揣,檢測到其與后一個標簽 B 發(fā)生了遮擋,此時需要執(zhí)行規(guī)避蔫缸。規(guī)避分為兩個階段:
Shift Down:計算得到標簽 A 與 B 的遮擋長度腿准,即 delta。將標簽 B 向下推移 delta 距離后拾碌,檢測后續(xù)的標簽 C 是否與其遮擋吐葱,如果存在遮擋則繼續(xù)推移標簽 C。由于標簽 C 推移后仍與標簽 D 存在一定間距校翔,因此 ShiftDown 階段停止于標簽 C弟跑。
Shift Up:ShiftUp 階段會從 ShiftDown 停止的標簽,即標簽 C 開始執(zhí)行向上推移防症,距離為 delta/2孟辑。同樣依次執(zhí)行推移直到其上的標簽與其存在間隔,上圖中停止于標簽 E蔫敲。
簡單來講饲嗽,ShiftDown 與 ShiftUp 兩個階段的含義就是當標簽發(fā)生遮擋時,將兩者以及其臨近的標簽各自推移一半的遮擋距離奈嘿。
優(yōu)先級
如果僅僅是自上至下對標簽依次執(zhí)行遮擋規(guī)避算法貌虾,就會出現(xiàn)這樣的情況:餅圖上部存在大量扇區(qū)面積較小的標簽,遮擋規(guī)避算法使得其占據(jù)了全部的畫布空間裙犹,餅圖下方扇區(qū)面積較大的扇區(qū)反而無法顯示尽狠。如下圖所示:上圖中占有較大扇區(qū)的標簽由于被擠壓超出了畫布無法展示。為了解決這一問題叶圃,有必要將所有的標簽進行優(yōu)先級排序袄膏。
標簽按照其對應扇區(qū)面積排序自不消說,標簽按照其優(yōu)先級依次放入畫布執(zhí)行遮擋規(guī)避也很自然掺冠。值得關(guān)注的問題在于:當上一個標簽執(zhí)行完畢碰撞規(guī)避后沉馆,當前標簽應當落在何處?
如果僅僅是簡單的將標簽放在默認的位置德崭,就可能導致扇區(qū)的先后順序與標簽的先后順序不對應悍及,其引導線自然也會出現(xiàn)交叉。為此接癌,應當同時保留標簽的優(yōu)先級順序以及對應扇區(qū)的順序。在標簽放入畫布時扣讼,尋找其扇區(qū)順序上的前后標簽:
如果前后標簽中間區(qū)域包含當前標簽的默認位置缺猛,則將當前標簽放入默認位置;
如果前后標簽中間區(qū)域不包含默認位置,必然出現(xiàn)了重疊導致標簽位置偏移荔燎,則將當前標簽緊貼前或后標簽耻姥。
為了保證高優(yōu)先級標簽不會被低優(yōu)先級標簽擠出畫布,還需要設(shè)置一個合理的布局 終止條件有咨。當標簽放入并執(zhí)行了碰撞規(guī)避后琐簇,程序會檢測畫布最頂端和最低端的標簽,并且嘗試將超出畫布的標簽向內(nèi)推移座享。如果一次推移之后仍舊存在超出畫布的標簽婉商,則可以認為此時畫布已經(jīng)過度擁擠,當前標簽不應當被顯示渣叛。此時便可以將所有標簽 Y 值恢復到上一輪標簽布局結(jié)束時丈秩,并終止新標簽的放入。
除了保證高優(yōu)先級的標簽有更高概覽展示之外淳衙,優(yōu)先級也提供了布局 **性能優(yōu)化 **的可能蘑秽。比起 ECharts 所采用的所有標簽一塊進行遮擋規(guī)避的方案,按照優(yōu)先級依次布局的方案不用在每一輪處理所有的標簽箫攀。同時肠牲,終止條件保證了在標簽數(shù)量極大的情況下,最多也僅需要處理畫布所能容納的最大標簽數(shù)量靴跛。除去優(yōu)先級排序的過程缀雳,最壞情況下,ECharts 布局的復雜度為 O(2N^2)汤求,而我們的方案則為 O(4K^2)俏险,其中 K 為畫布最大容納標簽數(shù)量。
切線限制
前頭談的都是標簽文字的布局扬绪,但是別忘了我們還有引導線的繪制竖独。雖然引導線僅僅是由連接標簽和扇區(qū)的輔助,但是其本身也有一定的美學要求挤牛,例如引導線不應當穿過餅圖的區(qū)域:
上圖給出了一個引導線影響餅圖繪制的極端示例莹痢。當然,這是一個舊算法下的執(zhí)行結(jié)果墓赴,在新的優(yōu)先級布局策略生效的情況下竞膳,結(jié)果可能不會變得如此糟糕,但是引導線遮蓋扇區(qū)的情況仍是難以避免的诫硕。
為了防止這一現(xiàn)象的發(fā)生坦辟,在每個標簽執(zhí)行遮擋規(guī)避之后,布局策略額外增加了切線的檢查章办。也即是以當前標簽對應扇區(qū)的中點為切點繪制切線锉走,并計算該切線到該側(cè)布局圓弧的交點滨彻,從而獲取到該標簽 Y 值的調(diào)整范圍。
在實現(xiàn)過程中挪蹭,這一策略會面臨一個問題:布局圓弧的半徑計算依賴于優(yōu)先級布局的結(jié)果亭饵,而在優(yōu)先級布局的過程中又需要布局圓弧半徑來做切線的檢查。這樣一來似乎陷入了“??生??生??”的困境梁厉。
這時候就需要我們給出一個額外的假設(shè)條件:當引導線穿越扇區(qū)的情況發(fā)生時辜羊,必然是由于標簽觸及到了畫布頂端或者底端并向畫布中間擠壓所導致。
基于這一條件词顾,用于切線檢查的圓弧半徑只需要設(shè)置為最大的圓弧半徑即可八秃。這樣一來,問題迎刃而解计技,剩下的便僅是重新?lián)炱鹬袑W數(shù)學的課本喜德,解幾個方程罷了。
不足之處
雖然目前的策略能夠合理的利用畫布空間展示標簽信息垮媒,但是也仍然存在某些可以改進的地方舍悯。
不支持自動換行
目前的布局策略支持多行文字的展示,但是并不支持文字的 自動換行睡雇。
由于我們的策略將標簽布局在圓弧上萌衬,因此標簽可繪制文字的剩余空間依賴于標簽 Y 值調(diào)整算法輸出的結(jié)果。自動換行生效時它抱,其會導致標簽高度的改變秕豫,又會重新觸發(fā) Y 值調(diào)整算法;而再一次調(diào)整結(jié)束后观蓄,標簽可能得到了足夠的空間混移,又變?yōu)閱涡酗@示,最終墜入了無限循環(huán)的地獄侮穿。
切線限制影響優(yōu)先級展示
在布局的過程中歌径,我們同時應用了優(yōu)先級的布局以及切線的限制。在某些特殊情況下亲茅,切線策略會導致標簽并不完全按照優(yōu)先級進行展示回铛,如下圖所示:
可以看到,相對而言具有較大扇區(qū)的濟寧由于切線策略的限制克锣,無法顯示標簽茵肃;但是底部扇區(qū)極小的上海卻能夠顯示。當然袭祟,這一點也很難說是一個 Bug验残,因為圖中的示例雖然打破了標簽顯示的優(yōu)先級,但是更好的利用了畫布空間巾乳。
標簽換邊
如果說前兩個問題是由于策略基本思想導致的難以解決的痼疾胚膊,或者僅僅是美學考量上的取舍故俐,那么標簽換邊則是一個后續(xù)切實可以改進的方面。如下如所示:
可以看到餅圖底部兩個扇區(qū)標簽并未顯示紊婉。這兩個標簽處在餅圖的右半邊,在這一半邊其優(yōu)先級不足辑舷,因此被舍棄喻犁。但是如果將其換到左半邊,則擁有較高的優(yōu)先級何缓,可以為其安排展示的空間肢础。但是哪些標簽需要換邊,標簽換邊之后是否會破壞原先的布局邏輯碌廓,仍然是一個需要好好討論的問題传轰。
數(shù)據(jù)平臺前端職位熱招中,掃碼進入我的 內(nèi)推渠道: