flowChart總結(jié)

核心思考

老實說,從現(xiàn)在的角度往回看,最初的時候我真的不知道自己想要做的是什么樣的東西 ……
我只是感覺,強烈地感覺,我能夠做出來點東西 ……

我花了一周多的時間做出來這個小工具:

demo1.png

所以,我得到的是一個什么樣的東西呢?(一本正經(jīng)地下個定義 …… )

從前端工程師的角度,它是一個以相對簡潔直觀的方式,輸入相對少的信息量,又能夠相對靠譜地適配大部分常規(guī)需求場景的網(wǎng)頁流程圖繪畫工具

求索

從使用者的角度,我想,判斷一個東西是否足夠有價值少不了考慮這兩個維度:是否足夠簡潔?是否足夠便利?
又因為當(dāng)前工具的目標(biāo)用戶是前端工程師,所以在便利性方面可以做少許妥協(xié) —— 即,簡化工程師向機器傳達思想的環(huán)節(jié)才是關(guān)鍵 ……

寫這么一個工具,有不同于寫一個常用的 table 組件的地方,因為 table 組件的“最少必要信息量”總是相對容易確定: 它需要一個表頭,以及每個表頭所對應(yīng)的每一行的值以及其它屬性

然而,繪畫一個流程圖的“最少必要信息量”是什么呢?一時間我想不明白。

在網(wǎng)絡(luò)上做檢索的過程中,我吸收了一些別人的思考:

1.chart1 - canvas
chart1.png

(因為中文網(wǎng)頁的抄襲版本太多,我搞不清楚作者是哪一位)

在這個例子中,我們可以直接告訴程序,這個節(jié)點放在哪里(行和列),以及線條應(yīng)該從哪里開始?如何走?又走到哪里?

// one step's info
{
    type:'Step',
    text:'業(yè)務(wù)名稱2',
    name:'step_2_2', // origin
    arrowArr:[
        {
            arrow:'drawBottomToLeft', // how to go 
            to:'step_3_2' // where to go 
        }
    ]
},
chart2 - flowChart.js

官網(wǎng):https://github.com/adrai/flowchart.js

chart2.png
// define every step
// &
// steps chain
const template = `
st=>start: Start|past:>http://www.google.com[blank]
e=>end: End:>http://www.google.com
op1=>operation: My Operation|past:$myFunction
op2=>operation: Stuff|current

st->op1(right)->cond
cond(yes, right)->c2
`

第二個例子看起來會簡便一些,定義節(jié)點和線條所需要的信息量會少很多,大部分設(shè)置是在基本配置里面統(tǒng)一完成的

3.chart3 - css

以上兩個流程圖都是通過 canvas 實現(xiàn)的,當(dāng)然還有 css 實現(xiàn)的:

源碼:https://codepen.io/demonwhite/pen/Gxqpzv

chart3.png

這是一個純 css 繪畫的流程圖,相比前面兩者,實現(xiàn)的難度會大一些,但是一旦實現(xiàn)了,想要進一步給節(jié)點添加效果卻非常方便。

結(jié)果

在本次求索的過程中,我漸漸得到了自己的思考:

首先,我需要一些前提條件:

1.不管節(jié)點(即“步驟”,下同)的大小如何,它所占據(jù)的區(qū)域是固定的

2.不追求適配所有情況,只滿足最基本的繪畫需求

3.允許程序完成大部分繪畫,然后由前端工程師手動微調(diào)

那么,畫一個流程圖的“最少必要信息量”可以是

1.基本參數(shù):節(jié)點默認寬高、節(jié)點之間的間隔

2.節(jié)點的內(nèi)容、類型、自定義屬性(寬高);

3.節(jié)點的位置(處于第幾行、第幾列);

4.線條連接哪兩個節(jié)點,以及線條的走向

落實到代碼層面是這樣的:

  1. 節(jié)點配置
const stepsOptions = [
    [{ content: '步驟 1', width: 100 }],
    [{ content: '步驟 2', }],
    [{ content: '步驟 3-1', width: 100 }, { content: '步驟 3-2', }, { content: '步驟 3-3', width: 100 }], // 第四行
    [{}, { content: '通過', }, { content: ' 不通過', }],
    [{ content: '結(jié)束', }],
]

比如,第四行代碼的意思是說:流程圖第三行有三個節(jié)點,以及每個節(jié)點的內(nèi)容

  1. 線條模板
const linesTemplate = `
1_1-(down)->2_1-(downLeftDown)-z>3_1-(downRightDown)-z>4_2-(right)->4_3-(rightUpLeft)-c>1_1
4_3-(downLeft)->5_1
2_1-(downRightDown)-z>3_3
3_2-(down)->4_1
3_3-(left)->3_2
4_1-(down)->5_1`

比如,第二行代碼的意思是,第四行第三個節(jié)點與第五行第一個節(jié)點,這兩個兩個節(jié)點之間,以‘右-下’的走向繪畫一根線條

demo2.png

你可能會發(fā)現(xiàn),利用這樣的寫法,實際上需要傳入的信息量比上面提到的 flowChart.js 第三方代碼更多一些 ……

我是這么考慮的:

不應(yīng)該在實用性維度上做過分的讓步,在我使用 flowCart.js 的過程中,我發(fā)現(xiàn)它極度便利,但是與此同時,它在樣式自定義方面上又薄弱到了極點 —— 它可以非常自動地計算出線條的合適走向,與之相應(yīng)的不得不做出的犧牲就是,我們很難去干預(yù)線條的走向;甚至我們只能在非常小的程度上改動它的節(jié)點樣式 ……

以上,

確定了傳入的信息量,接下來就是給計算器編碼一套“規(guī)則”,讓它生產(chǎn)出來我們想要的東西。

實現(xiàn)的思路

關(guān)于節(jié)點與線條的定位

在這里,定位的核心是采用 css 的 position ,首先寫一個 div,定一下寬高,設(shè)置 position: relative;。然后給所有 steps, lines 設(shè)置 position: absolute;,丟進去。

結(jié)果就是,所謂的“定位”問題,實際上就變成了數(shù)學(xué)計算問題,計算每一個 step 或 line 出現(xiàn)的位置、寬高(關(guān)鍵是起點坐標(biāo)(x, y)的計算)

畫節(jié)點

1.通過每一行的步驟數(shù),以及每個步驟對應(yīng)的 index,結(jié)合 step 的 width, 以 x = 0 未中心做定位

symmetry.png

2.利用黃金分割比,優(yōu)雅地計算出每一行距離頂端的高度 ……

height.png
畫線條

css 中的線條沒有我們想想中的那么好繪畫,借鑒了上文提到的 chart3,我采用 border 來繪畫線條

普通線條的或者有一個拐點的線條沒有特別的地方,主要是根據(jù)線條的走向然后利用 border-radius 讓拐點變得圓滑

.line {
    &.downRight, &.leftUp {
        border-radius: 0 0 0 5px;
        border-left: $borderStyle;
        border-bottom: $borderStyle;
    }
}

然而需要拐彎兩次的 z 形線和 c 形線就需要一點小技巧

在 z-形線條的實現(xiàn)上,我直接取用中間黃金分割點,拆分成兩個 div:

z-線條(1)


zline1.png

z-線條(2)


zline2.png

c-形線條的難點在于,在確定了起止點之后,它還需要繞一個大彎,關(guān)鍵點就在于,線條既不能橫穿其它節(jié)點,又能夠自動在合適的地方轉(zhuǎn)彎。
這里的實現(xiàn)是這樣的,拿左邊部分的拐彎來說,先找出起止節(jié)點之間的所有行中最寬的一行,以這一行的最左作為起點,找出這一點與圖形左邊緣之間靠近節(jié)點的黃金分割點,作為我們 C 形線條的中轉(zhuǎn)站 ……

黃金分割點的好處是 —— “效果總不會差”

c-線條(1)


cline1.png

c-線條(2)


cline2.png
畫箭頭

最初的思路是,讓箭頭也獨立出來,計算它在頁面上的定位,然后把它放進去,結(jié)果發(fā)現(xiàn),想要讓它剛剛好出現(xiàn)在線條與邊框交錯的位置,理論上可行,可現(xiàn)實上會比較麻煩,而且效果非常脆弱,容易出現(xiàn)樣式 bug。

于是我把它做成了線條 div 的一個偽類,根據(jù)線條的方向可以確定它出現(xiàn)的位置和方位:

&.downRight {
    // .arrow 用來判斷需不需要畫箭頭
    // ::after 實現(xiàn)箭頭效果
    &.arrow::after {
        right: -$lineWidth;
        @include arrowBottomOffset;
        @include arrowRight;
    }
}

一些思考

優(yōu)點

  1. 簡潔直觀的模板語法

1)節(jié)點屬性

根據(jù)節(jié)點在數(shù)據(jù)中的位置信息(第 i 個數(shù)組中的第 j 項),把節(jié)點映射到頁面上的位置(第 i 行第 j 排)

2)線條語法:

1_1-(downLeft)->2_1

相對簡潔直觀地表示起止節(jié)點以及線條的走向,開發(fā)者不用做任何數(shù)值計算就可以自動畫出一條符合大致預(yù)期的線條

  1. 樣式相對靈活,使用門檻相對低

與常規(guī)的流程圖繪畫(普遍 canvas 用得多)不同,這個工具采用的是 css 實現(xiàn),所以想要對流程圖結(jié)構(gòu)進行調(diào)整(比如修改樣式、添加樣式效果),實操起來會更為友好一些

從另外一個角度上考慮,大部分前端實際上是相對 canvas 更熟悉 css 的 ……

這個工具還提供了一個不錯的定位功能,可以快速定位到某一個步驟或線條所對應(yīng)的 div 數(shù)據(jù)

debug.png

不足

  1. 報錯機制

目前 line / cline / zline 容易寫錯,還沒提供有效的錯誤識別

  1. 半自動

目前這個小工具只能做到半自動,比如需要手動補充 vue 文件結(jié)構(gòu),引入 css, 并對 css 基本數(shù)據(jù)進行調(diào)整

一些思緒

復(fù)雜并不意味著更優(yōu),最初在構(gòu)思 Z, C 形線條的實現(xiàn)的時候,考慮到許多特殊情況,比如,如何讓 Z 線條不穿過別人呢?如果我的 C 線條不想在外面拐彎,我想在某兩個步驟之間的縫隙穿過呢?

也就是說,最初我想要做的是兼容各種奇奇怪怪的畫線兼容情況,考慮得太復(fù)雜,實現(xiàn)太難,以至于,思考老是進入死胡同。

直到,某一刻恍然大悟:我干嘛要追求面面俱到,我只要處理好最常規(guī)最簡單的情況就行了吧,一時間,前途似乎敞開了 ……

謝嘉鋒 ---- 2020-06-20 19:33:58

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容