設計稿(UI視圖)自動生成代碼方案的探索(ui界面生成)
設計稿(UI視圖)轉(zhuǎn)代碼是前端工程師日常不斷重復的工作,這部分工作復雜度較低但工作占比較高,所以提升設計稿轉(zhuǎn)代碼的效率一直是前端工程師追求的方向之一。
此前,前端工程師嘗試過將業(yè)務組件模塊化構建成通用視圖庫,并通過拖拽、拼接等形式搭建業(yè)務模塊,從而實現(xiàn)視圖復用,降低設計稿轉(zhuǎn)代碼的研發(fā)成本。但隨著業(yè)務的發(fā)展和個性化的驅(qū)動,通用視圖庫無法覆蓋所有應用場景,本文提出了一種設計稿自動生成代碼的方案。
1 背景
設計稿(UI視圖)轉(zhuǎn)代碼是前端工程師日常不斷重復的工作,這部分工作復雜度較低但工作占比較高,所以提升設計稿轉(zhuǎn)代碼的效率一直是前端工程師追求的方向之一。此前,前端工程師嘗試過將業(yè)務組件模塊化構建成通用視圖庫,并通過拖拽、拼接等形式搭建業(yè)務模塊,從而實現(xiàn)視圖復用,降低設計稿轉(zhuǎn)代碼的研發(fā)成本。但隨著業(yè)務的發(fā)展和個性化的驅(qū)動,通用視圖庫無法覆蓋所有應用場景,本文提出了一種設計稿自動生成代碼的方案。
目前,業(yè)內(nèi)主流的代碼生成方案有兩種,一種是通過訓練神經(jīng)網(wǎng)絡,從圖片或草圖直接生成代碼,以微軟Sketch2json為代表;另一種是基于Sketch源文件,從中解析出圖層信息轉(zhuǎn)化成DSL并生成代碼,以imgCook為代表。經(jīng)過實踐,我們發(fā)現(xiàn)第一種方案基于神經(jīng)網(wǎng)絡的代碼生成算法雖然簡單粗暴,但復雜層布局的準確率較低、可解釋程度不高導致后續(xù)無法持續(xù)優(yōu)化。方案二中Sketch源文件信息量豐富、算法自定義程度高、優(yōu)化空間大。因此,我們調(diào)研了業(yè)界基于Sketch的代碼自動生成方案(已對外公布或者開源),發(fā)現(xiàn)了一些不足并嘗試解決,下面從算法準確率、代碼可讀性、研發(fā)流程覆蓋度等方面做一下對比(該對比結(jié)果僅考察業(yè)界方案對我們自己業(yè)務的適用性,實際結(jié)果可能存在差異):
- 算法準確率方面:淘寶imgCook支持基于AI的組件識別,不支持成組布局,準確率中等(從官網(wǎng)了解到可以識別循環(huán)布局,但不能識別出測試樣本中的循環(huán)布局),58 Picasso僅支持原始組件的識別,復雜組件生成錯誤較多,不支持成組/懸浮/循環(huán)布局,準確率較低。
- 代碼可讀性方面:淘寶imgCook在生成布局時,測試樣本中圖層重疊區(qū)域使用到了基于根布局的絕對定位方式,不符合RD預期,可讀性一般,而我們的方案使用相對定位方式,可讀性較好。
- 研發(fā)流程覆蓋度方面:淘寶imgCook從RD視角構建了一個IDE,支持在IDE中完成樣式調(diào)整、邏輯綁定;而我們的方案從產(chǎn)研協(xié)作視角出發(fā),支持數(shù)據(jù)、邏輯、埋點的可視化配置及上線。
2 方案介紹
如圖所示,配置平臺主要分成三塊包括:設計稿轉(zhuǎn)視圖樹(UI2DSL)、視圖樹轉(zhuǎn)代碼(DSL2Code)、以及業(yè)務信息綁定,下面簡單介紹一下每一塊的作用。
- 設計稿轉(zhuǎn)DSL視圖樹(UI2DSL):將設計稿轉(zhuǎn)化成平臺無關的DSL視圖樹。
- 視圖樹轉(zhuǎn)代碼(DSL2Code):將DSL視圖樹轉(zhuǎn)化成基于Flex布局的MTFlexBox靜態(tài)代碼。
- 業(yè)務信息綁定:提供可視化配置工具,支持MTFlexBox靜態(tài)代碼綁定后臺數(shù)據(jù)、業(yè)務邏輯、以及曝光/點擊等埋點邏輯。
2.1 設計稿轉(zhuǎn)視圖樹(UI2DSL)
UI2DSL主要經(jīng)歷以下四個步驟:
2.1.1 設計稿導入
在日常開發(fā)過程中,我們接觸比較多的組件有按鈕、標題、進度條、評分組件等,但是Sketch數(shù)據(jù)源中并沒有這些組件只有圖層信息,圖層是設計師在設計UI視圖時用到的視圖控件。組件與圖層的對應關系是一對多的關系,圖層在Sketch數(shù)據(jù)源中的表現(xiàn)形式如下圖中的JSON數(shù)據(jù)結(jié)構所示,描述了圖層的坐標、大小等信息,后續(xù)布局生成就是基于對圖層的切割來實現(xiàn)的。
[ { "class_name":"MSTextLayer", "font_face":"PingFangSC-Medium", "font_size":13.44, "height":36.5, "index":8, "line_height":18.24, "name":"恒都民生精選豬小排帶骨400g±25g", "object_id":"EF55F482-A690-4EC2-8A6E-6E7D2C6A9D91", "opacity":0.9000000357627869, "text":"恒都民生精選豬小排帶骨400g±25g", "text_align":"left", "text_color":"#FF000000", "type":"text", "width":171.8, "x":164.2, "y":726.7 }, //......]
2.1.2 組件識別
從上面的數(shù)據(jù)源可以看出,圖層有圖片、文字、矩形等基本類型,在組件識別這一步圖層需要被轉(zhuǎn)化成文字/圖片/進度條/評分組件/價格組件/角標等日常開發(fā)使用的組件類型。但是目前我們的進展還停留在只能將圖層識別為文字或者圖片的階段,后續(xù)我們將接入淘寶開源的pipcook框架,基于神經(jīng)網(wǎng)絡算法進行更加豐富的組件類型識別。
2.1.3 可視化干預
設計稿作為輸入源是設計稿自動轉(zhuǎn)代碼的基礎,這對設計稿的設計規(guī)范要求較高。但在實踐中,我們發(fā)現(xiàn)設計師會利用Sketch中的基本圖形(每個圖形最終形成數(shù)據(jù)源中的一個圖層)疊加來描述一個組件的視覺效果,因此設計稿中不可避免會出現(xiàn)冗余圖層的問題,干擾DSL的生成。雖然我們也嘗試了利用自動化的手段刪除冗余圖層,但對于算法不能識別的部分(例如:圖片上有一個文本圖層,但是實際情況中文本是顯示在圖片里的,這個時候無法從算法層面決定是否刪除文本),仍然需要靠人工進行圖層刪除、合并等,否則無法正常生成DSL。設計稿主要有以下幾類問題。
圖層未合并
上圖是從設計稿解析出來的結(jié)果,可以發(fā)現(xiàn)在“美團優(yōu)選”文字上方的圖片中有很多紅色的矩形框(每個矩形框是一個單獨的圖層),而算法預期的輸入是一個圖層,因此需要在算法處理前將多個圖層合并成一個圖層,右側(cè)的三張圖也有類似問題。我們與設計同學進行過溝通,設計同學表示愿意在產(chǎn)出設計稿之前將圖層進行合并,但由于目前無法提供檢測機制(圖層合并是否有遺漏無法自動檢測出來),也就無法徹底避免圖層未合并的問題。
圖層位置交叉
實踐中發(fā)現(xiàn)當設計稿中不同字體/大小/顏色的文字排列在一起時,解析出來的圖層信息往往會出現(xiàn)重疊的情況,由于DSL視圖樹算法依賴位置來確定不同組件的約束關系,因此位置的交叉會對算法準確度造成較大的影響。
復雜背景圖層
上圖中紅色背景是由2個圖層(2個藍色矩形框)拼接形成的,左圖上的藍色圖層是純色,右圖上的藍色圖層是漸變色,在兩個圖層未合并的情況下,算法生成的代碼將會出錯。
上面提出的問題,通過約束設計師來達到設計稿的規(guī)范化,難度較大,所以我們提供了可視化干預工具。下面對上述問題做一個簡單的總結(jié):
- 問題一:圖層未合并問題肉眼很容易識別出來,利用工具將冗余圖層進行快速合并刪除即可。
- 問題二:圖層交叉問題肉眼不易識別,因此我們提供了檢測工具,基于檢測工具可以對設計稿中的交叉問題快速修復。
- 問題三:復雜背景問題肉眼不易識別,暫時也沒有有效的檢測工具,用戶可以采用邊干預邊生成的方式生成DSL。
可視化干預是UI2DSL重要的一環(huán),經(jīng)過可視化干預,將不標準的設計稿轉(zhuǎn)化為標準的圖層信息后再輸入給算法,可以極大地提升算法的準確率。 這里我們和imgCook的處理方式有一個區(qū)別:imgCook在引入了閾值處理等算法后(更智能,出錯概率更大),可視化干預能力主要體現(xiàn)在事后,而我們在生成DSL之前允許用戶對圖層進行干預,在干預時用戶面對的是直觀的圖層信息,可以有效降低工具的使用門檻(更穩(wěn)定,效果更好)。
2.1.4 視圖樹生成
將扁平的數(shù)據(jù)源轉(zhuǎn)化為樹狀結(jié)構的DSL,這個過程如果是人腦來做會怎么思考呢?先確定布局的整體結(jié)構是行布局或者列布局,然后再確定局部區(qū)域應該是什么布局結(jié)構,最后組裝起來形成視圖樹。這個過程與遞歸算法類似,因此我們采用了遞歸算法作為算法的主框架,同時引入了“橫豎切割 布局結(jié)構 模型評估”三大利器。
利器一:橫豎切割
生成DSL時采用了整分的思路,即將大布局不斷的切分成小布局,下面以動畫的形式看一下簡化過的DSL生成過程:
將設計稿一部分區(qū)域視為一個子區(qū)域,最開始的時候子區(qū)域和整個模板的面積一樣大,基于圖層的位置、大小信息,計算每個圖層的上/下/左/右邊緣坐標與其他圖層的相對關系,就可以尋找到切割點(如上圖中紅色箭頭所指的位置)。接下來依據(jù)切割點,將子區(qū)域切割成更小的子區(qū)域,在切割的過程中如果切割點是橫向的,則生成列布局;如果切割點是縱向的,則生成行布局。通過不斷的切割子區(qū)域得到更小的子區(qū)域,直到所有的子區(qū)域只剩下圖片或者文本等不可切割的圖層,這樣就可以生成完整的DSL視圖樹了。為了方便讀者理解,圖例中只演示了行布局、列布局的切分過程,實際情況還包含了其他布局類型,會要復雜許多。
這里還要注意一個問題,當有3個切割點時,我們選擇了直接將子區(qū)域切割成4個子區(qū)域,實際上我們可以只選擇1個切割點進行切割,也可以選擇2個切割點進行切割,當有N個切割點時,實際上存在(N的階乘 1)種切割方式,具體選擇哪種切割方式,我們會在利器三中討論。
利器二:布局結(jié)構
每個圖層都是一個矩形,為了生成布局結(jié)構只能依賴矩形的上下左右坐標信息。因此,對布局結(jié)構進行分類時,我們根據(jù)矩形與矩形之間的位置關系(相交、相離和包含關系)做了以下分類。
注意:從生成DSL的結(jié)果來看,包含布局和成組布局的處理方式其實是一樣的,都是使用類似于FrameLayout的層疊布局包含內(nèi)部圖層元素,但是我們?nèi)匀槐3址诸愒瓌t(矩形之間的位置關系)不變。
上圖中,相離、包含比較好理解,為什么兩個圖層相交的時候,會有成組和懸浮兩種類型的布局結(jié)構呢?我們看下上述成組布局、懸浮布局兩個設計稿中分別標出了相交的元素A、B,它們在位置上的相對關系是一樣的,都是A、B兩個圖層對應的矩形框發(fā)生了交叉。但是我們希望理想態(tài)的DSL視圖樹卻有所差異,如下圖所示:
- 成組布局中:A、B邏輯上是一個整體,交叉是必然的,最終DSL中A、B被層疊布局包含,層疊布局中沒有其他元素。
- 懸浮布局中:A、B邏輯上不是整體,只是碰巧交叉了,最終DSL中A、B分別在不同的層級中。
因此,對于圖層相交時可能有兩種類型的布局結(jié)構,分別是成組布局和懸浮布局。從上圖可以看出使用成組布局還是懸浮布局是由圖層內(nèi)容決定的,那么就需要算法理解圖層內(nèi)容了,比如基于AI構建樣本庫,記住所有的角標樣式(上面表格中4描述的),下次遇到角標相交時就生成成組布局??紤]到AI模型也是對規(guī)則的抽象,我們先搭建一套自定義識別規(guī)則。成組布局其位置信息是有規(guī)律可循的,例如:角標經(jīng)常出現(xiàn)在右上角,標簽經(jīng)常出現(xiàn)在左上角,頭像經(jīng)常橫向或者縱向交叉等,因此我們針對圖層之間的位置關系構建了交叉模型,如下圖所示:
上圖的交叉模型可以記住歷史模板中成組布局圖層之間的位置關系,下次遇到相交布局時判斷是否在歷史規(guī)則庫中即可完成識別,如果在就按成組布局處理否則按照懸浮布局處理。下圖是通過歷史模板構建的成組規(guī)則庫。
上面介紹了本方案中涉及的5種布局類型,目前來看這五種布局類型可以描述所有的模板布局,并且生成代碼符合RD的預期。下面展示兩個設計稿DSL實例:
利器三:模型評估
在介紹橫豎切割時,可以看到當存在多個切割點時,對所有切割點同時進行了切割,但實際上算法在切割時復雜度會更高,當有三個切割點時,實際上有5種切割方式,每種切割方式都會生成一個DSL。既然有5種切割方式,那么到底應該選擇哪一種DSL呢?模型評估算法就是用來解決這個問題的。
目前模型評估算法有兩個指標:布局節(jié)點數(shù)和逆布局指數(shù)。
- DSL中布局節(jié)點數(shù)越少,切割方式越好。
- 逆布局指數(shù)用來評估DSL中的行列布局的合理程度,其中逆布局指數(shù)越大越不合理,反之,逆布局指數(shù)越小,切割方式越好。
以下圖為例,看下視圖不同切割方式下對應的模型評估方式:
如果模型評估算法只衡量布局節(jié)點數(shù)的話,那么會選擇第一種切割方式生成的DSL作為最終的結(jié)果。但實際上,第二種切割方式更加合理。在切割方式一中,廣告、立即預約處于一個列布局中,但是橫向?qū)R方式(交叉軸)卻不一樣,“廣告”是右對齊,“立即預約”是左對齊,逆布局指數(shù)表示交叉軸對齊方式不一致的節(jié)點數(shù)量,因此通過逆布局指數(shù),我們可以規(guī)避掉不合理的切割方式。
2.1.5 列表布局
上一節(jié)介紹了基本的布局結(jié)構,雖然說這些布局結(jié)構已經(jīng)可以描述所有的UI布局,但是與RD的編碼習慣還是有一些差異。
對于上面的布局,RD通常不會把相同的item寫五遍,而是會將item放在一個類似于ListView的列表組件中,使代碼看起來簡潔易懂。因此在DSL生成階段,除了識別基本的行/列/包含/成組/懸浮布局外,還需要進一步識別行/列布局中的元素是否形成列表布局。在試驗過程中,我們發(fā)現(xiàn)列表布局分為兩種:單狀態(tài)列表組件和多狀態(tài)列表組件。上圖中每一個item的布局結(jié)構都是一樣的,我們稱為單狀態(tài)列表組件,再來看一下多狀態(tài)列表組件(如下圖所示),每個item有多種狀態(tài)(選中態(tài)和非選中態(tài)),并且不同狀態(tài)的布局結(jié)構不一致。
對行/列布局中單狀態(tài)列表組件的識別,只需要比較item子視圖樹的結(jié)構,子視圖樹結(jié)構一致則判斷為單狀態(tài)列表組件。對多狀態(tài)列表組件的識別我們采取了自動識別 人工干預的方式,自動識別的方式比較粗暴,只要行列布局中子item的寬/高接近,并且子item不是基本組件(基本組件容易形成誤判),就判定為多狀態(tài)列表組件。具體算法是計算子item寬高的標準差,小于閾值就判定為多狀態(tài)列表組件,否則不是。公式如下:
那為什么還要人工干預呢?因為是否使用列表組件其實與產(chǎn)品邏輯相關,但是目前我們無法將產(chǎn)品文檔中的邏輯識別出來,只能盡可能識別出所有的多狀態(tài)列表組件,并允許用戶對生成結(jié)果進行變更。比如上述送戀人的設計稿,產(chǎn)品可能約定每一個item都有選中態(tài)/非選中態(tài)兩種狀態(tài),也可能是從業(yè)務角度考慮需要著重突出送戀人這個item,這時每個item就只有一種確定的狀態(tài),這兩種不同的產(chǎn)品邏輯在編寫代碼時有不同的最優(yōu)技術方案。
2.2 視圖樹轉(zhuǎn)代碼(DSL2Code)
DSL視圖樹只是生成代碼的中間產(chǎn)物,還需要對DSL進行代碼還原,DSL2Code主要包括兩個步驟:屬性推斷、屬性信息調(diào)整。
2.2.1 屬性推斷
屬性推斷包括兩個部分:樣式屬性和結(jié)構屬性。樣式屬性包括字體、背景色、圓角等可以直接通過數(shù)據(jù)源信息中獲取得到的屬性;結(jié)構屬性包括大小、內(nèi)外邊距、主輔軸對齊等結(jié)構信息,這些信息無法從數(shù)據(jù)源中直接獲取,所以結(jié)構信息的推斷是這部分工作的重點。
結(jié)構信息推斷算法同樣使用遞歸算法作為主框架,通過一次遞歸對所有元素進行兩次遍歷來完成結(jié)構信息的推斷。如下圖所示,在對DSL所有節(jié)點進行遞歸遍歷時,把所有元素依次加入隊列中,遞歸完成后,再把所有節(jié)點依次移出隊列,這樣一進一出便對所有元素完成了兩次遍歷,我們把這兩次遍歷稱為進隊遍歷和出隊遍歷。
進隊遍歷時,推斷算法根據(jù)數(shù)據(jù)源中信息記錄每個節(jié)點的大小和位置信息,并根據(jù)位置關系計算每個子節(jié)點在父節(jié)點中期望的主輔軸對齊方式和內(nèi)外邊距。出隊遍歷時,父節(jié)點會根據(jù)子節(jié)點期望的對齊方式確定父節(jié)點最終的主輔軸對齊方式,并根據(jù)子節(jié)點的拉伸意圖修正父節(jié)點的大小。拉伸意圖即節(jié)點的大小不固定,根據(jù)顯示內(nèi)容不同,在水平或垂直方向上可能會變大或變小,例如文本節(jié)點根據(jù)顯示字數(shù)的多少長度會發(fā)生變化,字數(shù)過多時甚至還會換行。
2.2.2 屬性信息調(diào)整
由于輸入源是基于設計稿呈現(xiàn)的靜態(tài)效果圖,設計稿中每個元素缺失了真實的業(yè)務含義,同樣的展示效果在不同的業(yè)務場景中會有不同的屬性要求,對于這部分內(nèi)容,我們無法從輸入源中進行準確推斷。為此,我們提供了可視化的屬性信息調(diào)整功能來輔助代碼生成,頁面效果如下圖所示,在這個頁面可以對DSL中的所有節(jié)點屬性進行查看和修改調(diào)整。
經(jīng)過業(yè)務信息補充后,便可進行最后的自動代碼轉(zhuǎn)化,通過語法映射自動把DSL轉(zhuǎn)化成MTFlexbox模板代碼。
3 成果展示
下面是設計稿直接生成代碼未經(jīng)修改展示后的手機屏幕截圖,可以看到取得了不錯的還原效果:
以上就是我們近期對代碼自動生成的探索及實踐,后續(xù)我們將引入機器學習及神經(jīng)網(wǎng)絡算法,對過程進行進一步優(yōu)化。如果您有其他的看法或建議,也歡迎在文末進行評論,或者跟我們聯(lián)系。
作者簡介
田貝、少寬、騰飛等,美團平臺終端業(yè)務研發(fā)團隊研發(fā)工程師。
團隊簡介
美團平臺終端業(yè)務研發(fā)團隊的職責是保障平臺業(yè)務高效、穩(wěn)定迭代的同時,持續(xù)優(yōu)化用戶體驗和研發(fā)效率。團隊負責的業(yè)務主要包括美團首頁等千萬級DAU高頻業(yè)務以及分享、賬號、音/視頻等基礎業(yè)務,支撐和對接外賣、酒店等30多個業(yè)務方。團隊通過動態(tài)化能力建設,加快業(yè)務上線速度,幫助產(chǎn)品(PM)快速驗證業(yè)務選型,做出業(yè)務決策;架構/服務標準化體系建設,提升前后端以及平臺與業(yè)務線的溝通、合作效率;業(yè)務監(jiān)控和體驗優(yōu)化,有效保障核心業(yè)務服務成功率的同時,提升用戶使用美團App過程中的穩(wěn)定性和流暢性。團隊開發(fā)技術棧包括Android、iOS、React Native、Flexbox等。
招聘信息
美團平臺終端業(yè)務研發(fā)團隊是一個活力四射、對技術充滿激情的團隊,現(xiàn)誠聘Android、iOS、FE工程師,歡迎有興趣的同學投簡歷至tech@meituan.com(備注:美團平臺終端業(yè)務研發(fā)團隊)。
—
本文系美團技術團隊出品,著作權歸屬美團。歡迎出于分享和交流等非商業(yè)目的轉(zhuǎn)載或使用本文內(nèi)容,敬請注明“內(nèi)容轉(zhuǎn)載自美團技術團隊”。本文未經(jīng)許可,不得進行商業(yè)性轉(zhuǎn)載或者使用。任何商用行為,請發(fā)送郵件至tech@meituan.com申請授權。