10 倍高清不花!大麥端選座 SVG 渲染

10 倍高清不花!大麥端選座 SVG 渲染

作者 | 阿里文娛無線開發專家 波濤

出品 | CSDN(ID:CSDNnews)

10 倍高清不花!大麦端选座 SVG 渲染

背景介紹

用戶在大麥上購票,需要自行選座。在大型場館下,如何讓 10 萬+座位繪製達到閃開?這需要技術在繪製上保證性能流程,在選座渲染上通過技術手段賦予更多可能性。因此,大麥引用 SVG 繪製技術,並根據業務場景下作了很多優化,本文是大麥在用戶端的技術方案設計與應 用實踐。

10 倍高清不花!大麦端选座 SVG 渲染

10 萬+座位繪製面臨以下挑戰

  1. 如何豐富標籤樣式及屬性;

  2. SVG 渲染性能優化;

  3. SVG 如何與業務場景結合;

  4. 如何將 CSS 能力應用到 SVGKit,保持(iOS\\Android\\H5)一致性。

10 倍高清不花!大麦端选座 SVG 渲染

大麥 C 端場景下 SVG 應用

1. SVG 介紹

可伸縮矢量圖形 (Scalable Vector Graphics),用來定義用於網絡的基於矢量的圖形,使用 XML 格式定義圖形,圖像在放大或改變尺寸的情況下其圖形質量不會有所損失,是萬維網聯 盟的標準, DOM 和 XSL 之類的 W3C 標準是一個整體,不失真,兼容現有圖片能力前提還 支持矢量(瀏覽器兼容情況),通過瀏覽器很早版本支持情況在主流瀏覽器都支持,SVG 提供的 功能集涵蓋了嵌套轉換、裁剪路徑、Alpha 通道、濾鏡效果等能力,它還具備了傳統圖片沒有 的矢量功能,在任何高清設備都很高清。

10 倍高清不花!大麦端选座 SVG 渲染

圖 1 SVG 與其他格式圖片比較

2. SVGKit 使用

瀏覽器默認就支持 SVG 渲染,屬於 XML-Dom 家族系列,但是在移動端上並沒有做原生支 持,還是按照 XML 進行的讀取,支持的開源庫也不多,在 IOS 上,目前 OC 版本 SvgKit 還不 錯,官方 Github 也在繼續維護,雖然更新較慢,通過幾次 patch 提交 PR 還是很快 merge 的, 一些通用屬性和控件支持的不夠完善,需要進行定製開發,swift 版本的 macaw 也不錯,在動畫 效果上更加酷炫,目前也正在做 swift 效果遷移到 OC 中,渲染流程如下:

10 倍高清不花!大麦端选座 SVG 渲染

圖 2 SVGKit 渲染加載流程圖

1)SVGKit 有哪些標籤?

<code>circle = SVGCircleElement; 【圓形】/<code><code>clipPath = SVGClipPathElement;【層疊路徑】 description = SVGDescriptionElement; 【描述】 ellipse = SVGEllipseElement; 【橢圓】/<code><code>g = SVGGElement; 【容器標籤】/<code><code>line = SVGLineElement;【直線】 path = SVGPathElement;【路徑】/<code><code>polygon = SVGPolygonElement;【多角形】 polyline = SVGPolylineElement;【多邊形】 rect = SVGRectElement;【矩形】/<code><code>svg = SVGSVGElement;【SVG 容器標籤】 switch = SVGSwitchElement;【選擇】 text = SVGTextElement;【文本】/<code><code>textArea = TinySVGTextAreaElement;【區域文本】 title = SVGTitleElement;【標題】/<code><code>2)擴展基於三端統一 SVG 標籤和屬性/<code>
10 倍高清不花!大麦端选座 SVG 渲染

圖 3 SVG 標籤屬性擴展大圖

3)SVG 標籤在 SVGKit 中渲染流程

a)SVGKit 核心渲染原理分析

視圖 SVGKFastImageView.m 加載到窗口顯示 核心中心處理類,主要加載 SVG 文件資源文件

<code>類:SVGKimage : NSObject/<code><code>SVGKParseResult* parsedSVG = [parser parseSynchronously]; // 解析 /<code><code>SVGKImage* finalImage = [[SVGKImage alloc] initWithParsedSVG:parsedSVG /<code><code>fromSource:source];/<code>

b)分析:

<code>解析 SVG-到合成 ViewLayer/<code><code>初始化 SVGKSource source svg 資源實例 初始化 SVGSVGElement -> DomTree 初始化 SVGDocument -> DomDocument CALayerTree 最終合成的 Layer 樹/<code><code>SVGKParser* parser = [SVGKParser newParserWithDefaultSVGKParserExtensions:source]; 開 始解析/<code>

c)解析 XML 類 SVGKParser:NSObject 解析 SVG(XML)文件

<code>+(SVGKParser ) newParserWithDefaultSVGKParserExtensions:(SVGKSource )source (SVGKParseResult*) parseSynchronously. 解析異常處理 XML 解析處理/<code><code>XML 解析過程 SAX/<code><code>// 每解析一個 Node 添加到 DOMTree 中. (SVGKParserStyles) SVGKParserDefsAndUse 【解析 useAndDefs 樣式】 SVGKParserDOM 【解析 DOM】 SVGKParserGradient【解析漸變標籤】 SVGKParserPatternsAndGradients【解析圖案】 SVGKParserStyles【解析樣式】/<code><code>SVGKParserSVG【解析 SVG 標籤】/<code><code>d)解析 XML 中 CSS 樣式類 SVGKParserStyles :/<code><code>標籤解析到生成 Layer 層 1.類:SVGKParserDOM.m: SVGElement. 2.核心思想:/<code><code>SVG 標籤渲染流程一、SVGKImage.m 渲染 核心思想:遍歷 DOM 映射到 iOS layer 繪製/<code><code>3.生成 UILayer:/<code><code>-(CALayer *)newCALayerTree/<code><code>CALayer* newLayerTree = [self newLayerWithElement:self.DOMTree]; CALayer sublayer = [self newLayerWithElement:(SVGElement )child]; [newLayerTree addlayer. Sublayer]/<code><code>[element layoutLayer:layer]; [layer setNeedsDisplay];/<code>

4)SVGKit 分析總結

SVGKit 版本升級 2.X 升級 3.X-Release,升級後主要是一些屬性的支持度更完善,包括 Text 富文本渲染,字體多樣式支持,還有一些渲染上的優化,可通過 patch 提交 查看,比較一下 W3C 下 SVG 圖在 2.X 分支及 3.X 分支的解析及渲染時間,性能能也有提升, 同時增加 image 圖片加載 base64 圖片,加載在線 URL 及本地資源圖片,我們也在 SVGImageElement 中提供擴 展 API 增加 Webp 支持,因為 SVG 本身是為矢量圖方案加入 PNG 等圖存在一定模糊情況,不 過運營可能會在底圖上做一些 Logo 展示,為了減少 SVG 編輯複雜度,做了一些 png\\jpg 圖的 嵌入,一般圖片都不大,以下 API:展示 base64 運營位圖片而設計的

<code> [NSData dataContentWithBase64Str:str]/<code>

3. 基於 CSS 著色能力

1)為什麼用 CSS 著?

SVG 雖然是繪製圖形,原理如同 HTML,是給每一個標籤設置一個單獨 style 好還是通過 CSS Id /class 映射好呢,這個思路和 HTML 處理 STYLE 樣式是一樣的,便於更改和維護,在 性能上也更好,同時增加了 important 屬性,可以更好的配置樣式,可做到運營側根據樣式 style 下發方式達到更改 SVG 圖效果,可以做到更多活動效果及個性化需求。

2)SVG-CSS 著色渲染過程

SVG 標籤基於 CSS 樣式快速應用,通過遍歷 DomTree,找到對應的 Node 節點,在給 node 節點設置 id 或者 class,然後局部刷新 Tree 父節點,實現換色,細節流程如下

10 倍高清不花!大麦端选座 SVG 渲染

圖 4 CSS 著色原理與時序圖

3)大麥端選座渲染效果

10 倍高清不花!大麦端选座 SVG 渲染

圖 5 CSS 著色渲染效果

4)CSS 著色原理總結及性能比較 如何確定屬性使用的是 CSS 顏色還是自帶 style 屬性?

當 SVG 在解析生成 DomTree 後,我們可以根據 CSSStyle 樣式存儲的 CSS 樣式,給 Node 標籤設置 id 及 class,當更改 nodeList 後,相當於樹結構進行了修改,在繪製時候查找屬性會根 據優先策略 id > class > 進行查找進行屬性賦值,我們根據 CSS 屬性 !important 來設置最高優 先級,這樣就避免了此問題。

端側渲染流程如下,左側:是基於 node 遍歷後修改,右側是修改 id/class 方式【推薦】。性能比對:為了兼容 W3C 標準,端上增加了 CSS 特殊屬性 important。

10 倍高清不花!大麦端选座 SVG 渲染

圖 6 SVG-Codec 總體性能提升對比

4. SVG 約束 DTD

1)背景介紹

當 SVG 生產端在製作 SVG 圖,可能會用到 Adobe 等軟件,有很多複雜屬性及層疊,可能 會產生複雜 XML 格式,這樣在渲染過程中會造成大量遍歷,影響性能,也有一些特殊屬性, 端上並沒有支持,例如濾鏡、動畫,這樣,我們就需要有一種約束來校驗生產和渲染 SVG 能夠 一致。

2)文檔類型定義

(DTD)可定義合法的 XML 文檔構建模塊。它使用一系列合法的元素來定義文檔的結構。

DTD 可被成行地聲明於 XML 文檔中,也可作為一個外部引用。

DTD 被定義在 xml 的 DOCTYPE 聲明中。

3)定義一個名為 note 的 DTD

如果要使用內部定義,則在 xml 文件的 xml 版本聲明頭下面添加如下代碼塊:

// DTD 內容]> 如果要引用外部 DTD,那麼它應通過下面的語法被封裝在 一個 DOCTYPE 定義中: 例如,在 xml 文件的 xml 版本聲明頭下面添加如下代碼塊:

一個 DTD 的內容示例:

<code> /<code><code><code><code><code>

其中:

!ELEMENT note 定義 note 元素有四個元素:"to、from、heading、body"

!ELEMENT to 定義 to 元素為 "#PCDATA" 類型。

PCDATA 的意思是被解析的字符數據(parsed character data)。可想象為 XML 元素的開始 標籤與結束標籤之間的文本,PCDATA 是會被解析器解析的文本。這些文本將被解析器檢查以 及標記,文本中的標籤會被當作標記來處理,而實體會被展開,不過,被解析的字符數據不應 當包含任何 &、< 或者 > 字符;需要使用 &、< 以及 > 實體來分別替換它們。

它表示在<name>和/<name>之間可以插入字符或者子標 籤,CDATA 的意思是字符數據(character data, CDATA 是不會被解析器解析的文本。在這些文 本中的標籤不會被當作標記來對待,其中的實體也不會被展開。

5)如何校驗

在完成 DTD 文件的編寫後,就是使用 DTD 了,1、 一般使用代碼解析的方式,進行 DTD 對 xml 的規範性校驗,首先在 svg 的頭部加入:然後 在解析 svg 時,聲明合法性校驗例如,以 SAX 解析 XML 為例:

<code>SAXParserFactory spf = SAXParserFactory.newInstance; spf.setValidating(true); // 關鍵設置/<code><code>SAXParser sp = spf.newSAXParser; XMLReader xr = sp.getXMLReader;/<code><code>XMLParser.SAXHandler handler = new XMLParser.SAXHandler; xr.setContentHandler(handler);/<code><code>xr.setErrorHandler(new SAXErrorHandler); // 輸出校驗出錯的信息/<code><code>xr.setProperty("http://xml.org/sax/properties/lexical-handler", handler); xr.parse(new InputSource(is));/<code>

其中,必須設置 setValidating(true);才能使 DTD 校驗 xml 生效,為方面使用,提供了可以動 態讀取 dtd 的方式,為不需要將 dtd 信息添加到 svg 的文件中:執行 Java -jar 命令,傳入兩個參 數:一個是 svg 的全路徑;一個在同目錄下的 dtd 的文件名(帶擴展名)。

5. 選座性能優化

1)性能調研:APP 端/H5 上渲染如何解決 10 萬座位渲染,端側通過組件複用,手機設備 性能天然還是不錯的,加上我們通過預加載資源與分區加載結合方式,點擊區域後進行繪製策 略,避免一次性加載全量 10 萬數據,也給用戶更好的交互體驗,然而 H5 側,瀏覽器就不那麼 流暢了,隨著 H5 技術發展,H5 新特性的支持,通過實踐使用 SVG 方案,每個座位都用一個 svg 元素顯示,由於 svg 的矢量特性,縮放無鋸齒,展現效果比較好,但也就支持到 3 萬左右的 座位,座位再多也會出現渲染慢和縮放卡頓等問題。Svg 的每個元素也算是一個瀏覽器的 dom, dom 數量一多起來,達到 3 萬到 10 萬,瀏覽器渲染顯然不行。

如何減少 dom 節點,又能顯示 這麼多內容呢?很容易就能想到用 Canvas 來繪製座位,無論在 Canvas 繪製多少元素,在瀏覽 器都是一個元素,這樣就很好解決了 dom 數量的問題,網上能搜到多個應用比較多的開源 Canvas 組件,能實現繪製元素的實時更新,還支持元素的鼠標/手勢時間響應。但是都有一個問題,基 本上的原理都是採用幀刷新重繪 Canvas 畫布的方式來實現視圖更新,確定採用 Canvas +svg 渲 染座位圖,模擬了 10 萬座位的渲染並實現了縮放拖拽等操作,達到了預期的效果。

10 倍高清不花!大麦端选座 SVG 渲染

圖 7 選座性能優化-預加載

10 倍高清不花!大麦端选座 SVG 渲染

圖 8 端選座性能交互圖

6. SVG 場館彩虹圖實現

1)SVG 彩虹圖介紹:

在售票選座業務中,需要為用戶顯示場館圖(SVG 格式),給用戶一個場館的整體印象, 同時方便用戶選擇場館的看臺,進而展示看臺座位進行選座。但是,在使用彩虹圖展示場館圖之 前,場館圖的看臺區域僅展示看臺當前可售座位中的最高票價對應的顏色進行展示,如下圖所示:

10 倍高清不花!大麦端选座 SVG 渲染

圖 9 非彩虹場館圖

每個看臺都是單一顏色的,不能反映出當前看臺中可售的座位的價格分佈情況,很容易迷 惑用戶,造成每個看臺只有一種票價的印象,同時不方便用戶快速定位他的目標價位所在的區 域。為了準確的反映出場館每個看臺的價格情況,需要將看臺中所售的所有的座位的價格展示 出來,因此,採用彩虹圖的形式。目標效果如下所示:

10 倍高清不花!大麦端选座 SVG 渲染

圖 10 彩虹場館圖

每個區域的所有的座位價格以彩虹的形式顯示出來,相比以前的只顯示最高票價的顏色, 彩虹圖可以清晰的展示每個區域中座位的價格情況。

2)總體思路:在每個看臺區域中以彩虹圖形式展示多個顏色,就需要將每個看臺區域進行劃分,放棄之前用一個這類的繪製標籤來展示一個看臺區域,一個區域內應該包含多個排,對每個排按照座位價格進行著色,進而對每個看臺進行同樣的處理,總體上形成彩虹樣式展示。因此,一個看臺區域,應該是多個 svg 標籤組合而成的。如圖:

10 倍高清不花!大麦端选座 SVG 渲染

圖 11 SVG 文件說明

對 SVG 底圖進行改造,將老的一個 SVG 標籤代表一個看臺區域的形式,改造成每個看臺 區域由標籤進行包裹的多個標籤的組合。

為了方便降級,處理不顯示彩虹圖的業務需求,同時約定標籤下的第一個標籤,表示整個 區域,同時不再解析渲染後面的排信息。

3)算法生成彩虹圖方案 a)介紹:

彩虹圖生成算法主要是通過座位的分佈和座位的票檔圈出一個看臺中相同票檔座位的範 圍,然後生成一個 path 路徑。將這些 path 路徑加入到 svg 底圖中去並且和相應的票檔綁定,就 能實現一個區域多種顏色的效果。

b)步驟如下:

對看臺中所有排和座位進行分組排序;

②計算看臺方向;

③獲取同種顏色座位邊界;

④計算色塊的方向;

⑤獲取色塊的路徑;

⑥生成看臺的彩虹圖效果;

⑦遍歷所有看臺生成完整的彩虹圖。首先將某個看臺所有座位按排分組,然後將排按排號從小到大進行排序。數據結構如下:

10 倍高清不花!大麦端选座 SVG 渲染

圖 12 看臺編號與座位號示例

座位數據是必備的基礎數據,後續一切的計算都依賴於座位數據。通過第一排和第二排座位的相對位置算出看臺的方向。

比如:拿到 1 排 1 號和 2 排 1 號座位的座標,從 2 排 1 號向 1 排 1 號畫一條射線,這個射線 的角度就當做看臺的方向。

後期如果在生產 SVG 的時候將舞臺位置標記出來的話就可以利用舞臺來確定看臺方向。

10 倍高清不花!大麦端选座 SVG 渲染

圖 13 看臺方向-1

每個看臺的座位分佈可以分成兩種,一排一種價格和一排多種價格。其中一排一種價格的情況就以同色最後一排為邊界。如下圖綠色的線。一排多種價格的情況就需要把每一排不同顏色轉換處的那兩個點記錄下來連成邊界線。如下圖紅色和黃色的線。

10 倍高清不花!大麦端选座 SVG 渲染

圖 14 看臺方向-2

紅黃綠三條線所在的座位就是我們需要的色塊邊界。拿到色塊邊界座位數組之後還需要知道色塊的角度。一排同色的色塊方向就直接使用看臺

的方向。一排多色的色塊方向計算方法如下:

10 倍高清不花!大麦端选座 SVG 渲染

圖 15 確定顏色區域-1

將第一個座位 P1 和最後一個座位 P2 連線,取這個線段的垂直線 a1 和 a2。用這兩條垂直 線分別和看臺方向取夾角。夾角小於 90 度的垂直線的角度作為色塊的方向。圖中 a1 就是色塊 的方向。得到色塊邊界和色塊方向後就可以計算色塊 path 的路徑了。

①得到包含這個看臺 path 路徑的最小矩形 rect (圖 1);

②從上一步獲取的座位邊界數組中取四個點分別為:第一個點 P1,第二個點 P2,倒數第二 個點 P3,最後一個點 P4 (圖 2);

③由 P2 向 P1 方向做一條射線得到和 rect 的交點 A1,由 P3 向 P4 方向做一條射線得到和

rect 的交點 A2 (圖 2);

④根據色塊方向獲取色塊路徑的幾個關鍵點:A1 A2 A3 (圖 2);

⑤將幾個關鍵點和座位邊界所有點連接起來生成一個閉合的路徑(圖 3 橙色線框)。

10 倍高清不花!大麦端选座 SVG 渲染

圖 16 確定顏色區域-2

拿到所有色塊路徑後就可以將色塊填充對應的顏色並且按順序疊加到看臺上形成彩虹圖效果。

色塊的疊加方式如下:色塊的生成順序是 path1->path2->path3->path4,然後倒序疊加到看臺 上 path4->path3->path2->path1。最後就是遍歷所有的看臺,生成一張完整的彩虹圖。

10 倍高清不花!大麦端选座 SVG 渲染

圖 17 確定顏色區域-3

10 倍高清不花!大麦端选座 SVG 渲染

總結

本文主要講解了大麥核心鏈路選座 SVG 應用,並結合實際場景做了一些創新嘗試,包括:豐富 SVG 應用的業務場景、SVG 標籤屬性及擴展、CSS 著色、渲染性能優化等,目的是讓端解析接近瀏覽器解析效果,並提供更好的端選座性能體驗。

10 倍高清不花!大麦端选座 SVG 渲染

☞斬獲GitHub 2000+ Star,阿里雲開源的 Alink 機器學習平臺如何跑贏雙11數據“博弈”?| AI 技術生態論

☞2020 年,AI 芯片內存哪家強?

☞拜託,別再問我什麼是 B+ 樹了

☞程序員為什麼應該旗幟鮮明地反對“最佳實踐”?

☞半小時訓練億級規模知識圖譜,亞馬遜AI開源知識圖譜嵌入表示框架DGL-KE

☞“出道” 5 年採用率達 78%,Kubernetes 的成功秘訣是什麼?

☞警惕!新騙術出現:這些虛假二維碼生成器已成功盜取 4.6 萬美元!

今日福利:評論區留言入選,可獲得價值299元的「2020 AI開發者萬人大會」在線直播門票一張。 快來動動手指,寫下你想說的話吧。


分享到:


相關文章: