Categories
程式開發

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


一、背景介紹

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

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

  1. 如何豐富標籤樣式及屬性;
  2. SVG 渲染性能優化;
  3. SVG 如何與業務場景結合;
  4. 如何將 CSS 能力應用到 SVGKit,保持(iOSAndroidH5)一致性。

三、大麥 C 端場景下 SVG 應用

  1. SVG 介紹

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

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

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

  1. SVGKit 使用

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

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

圖 2 SVGKit 渲染加載流程圖

1)SVGKit 有哪些標籤?

circle = SVGCircleElement; 【圆形】
clipPath = SVGClipPathElement;【层叠路径】
description = SVGDescriptionElement; 【描述】
ellipse = SVGEllipseElement; 【椭圆】
g = SVGGElement; 【容器标签】
image = SVGImageElement;【图片】
line = SVGLineElement;【直线】
path = SVGPathElement;【路径】
polygon = SVGPolygonElement;【多角形】
polyline = SVGPolylineElement;【多边形】
rect = SVGRectElement;【矩形】
svg = SVGSVGElement;【SVG 容器标签】
switch = SVGSwitchElement;【选择】
text = SVGTextElement;【文本】
textArea = TinySVGTextAreaElement;【区域文本】
title = SVGTitleElement;【标题】

2)擴展基於三端統一 SVG 標籤和屬性

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

圖 3 SVG 標籤屬性擴展大圖

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

a)SVGKit 核心渲染原理分析

視圖 SVGKFastImageView.m 加載到窗口顯示

核心中心處理類,主要加載 SVG 文件資源文件

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

b)分析:

解析SVG-到合成ViewLayer
初始化 SVGKSource source svg 资源实例
初始化 SVGSVGElement -> DomTree
初始化 SVGDocument -> DomDocument
CALayerTree 最终合成的Layer 树
SVGKParser* parser = [SVGKParser newParserWithDefaultSVGKParserExtensions:source]; 开始解析

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

+(SVGKParser ) newParserWithDefaultSVGKParserExtensions:(SVGKSource )source
(SVGKParseResult*) parseSynchronously. 解析异常处理XML 解析处理
XML 解析过程 SAX
// 每解析一个Node 添加到DOMTree 中. (SVGKParserStyles)
SVGKParserDefsAndUse 【解析useAndDefs 样式】
SVGKParserDOM 【解析DOM】
SVGKParserGradient【解析渐变标签】
SVGKParserPatternsAndGradients【解析图案】
SVGKParserStyles【解析样式】
SVGKParserSVG【解析SVG 标签】

d)解析 XML 中 CSS 樣式類 SVGKParserStyles :

标签解析到生成Layer 层
1.类:SVGKParserDOM.m: SVGElement.
2.核心思想:
SVG 标签渲染流程一、SVGKImage.m 渲染 核心思想:遍历DOM 映射到iOS layer 绘制
3.生成UILayer:
-(CALayer *)newCALayerTree
CALayer* newLayerTree = [self newLayerWithElement:self.DOMTree];
CALayer sublayer = [self newLayerWithElement:(SVGElement )child];
[newLayerTree addlayer. Sublayer]
[element layoutLayer:layer];
[layer setNeedsDisplay];

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 編輯複雜度,做了一些pngjpg 圖的嵌入,一般圖片都不大,以下API:展示base64 運營位圖片而設計的

[NSData dataContentWithBase64Str:str]
  1. 基於 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

圖 4 CSS 著色原理與時序圖

3)大麥端選座渲染效果

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

圖 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

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

  1. SVG 約束 DTD

1)背景介紹

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

2)文檔類型定義

(DTD)可定義合法的 XML 文檔構建模塊。它使用一系列合法的元素來定義文檔的結構。 DTD 可被成行地聲明於 XML 文檔中,也可作為一個外部引用。

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

3)定義一個名為 note 的 DTD

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

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

一個 DTD 的內容示例:






其中:

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

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

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

4)例如在 DTD 中聲明

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

5)如何校驗

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

SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setValidating(true); // 关键设置
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
XMLParser.SAXHandler handler = new XMLParser.SAXHandler();
xr.setContentHandler(handler);
xr.setErrorHandler(new SAXErrorHandler()); // 输出校验出错的信息
xr.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
xr.parse(new InputSource(is));

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

  1. 選座性能優化

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

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

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

圖 8 端選座性能交互圖

  1. SVG 場館彩虹圖實現

1)SVG 彩虹圖介紹:

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

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

圖 9 非彩虹場館圖

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

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

圖 10 彩虹場館圖

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

2)總體思路:

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

因此,一個看台區域,應該是多個 svg 標籤組合而成的。如圖:

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

圖 11 SVG 文件說明

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

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

3)算法生成彩虹圖方案

a)介紹:

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

b)步驟如下:

①對看台中所有排和座位進行分組排序;

②計算看台方向;

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

④計算色塊的方向;

⑤獲取色塊的路徑;

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

⑦遍歷所有看台生成完整的彩虹圖。

首先將某個看台所有座位按排分組,然後將排按排號從小到大進行排序。數據結構如下:

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

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

座位數據是必備的基礎數據,後續一切的計算都依賴於座位數據。

通過第一排和第二排座位的相對位置算出看台的方向。

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

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

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

圖 13 看台方向-1

每個看台的座位分佈可以分成兩種,一排一種價格和一排多種價格。

其中一排一種價格的情況就以同色最後一排為邊界。如下圖綠色的線。

一排多種價格的情況就需要把每一排不同顏色轉換處的那兩個點記錄下來連成邊界線。如下圖紅色和黃色的線。

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

圖 14 看台方向-2

紅黃綠三條線所在的座位就是我們需要的色塊邊界。

拿到色塊邊界座位數組之後還需要知道色塊的角度。一排同色的色塊方向就直接使用看台的方向。一排多色的色塊方向計算方法如下:

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

圖 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

圖 16 確定顏色區域-2

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

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

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

圖 17 確定顏色區域-3

四、總結

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

作者簡介

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

相關鏈接

10W 座位的大場館究竟是怎麼畫出來的?