用户在大麦上购票,需要自行选座。在大型场馆下,如何让10万+座位绘制达到闪开?这需要技术在绘制上保证性能流程,在选座渲染上通过技术手段赋予更多可能性。因此,大麦引用SVG绘制技术,并根据业务场景下作了很多优化,本文是大麦在用户端的技术方案设计与应用实践。
10万+座位绘制面临以下挑战
如何丰富标签样式及属性;
SVG渲染性能优化;
SVG如何与业务场景结合;
如何将CSS能力应用到SVGKit,保持(iOS\Android\H5)一致性。
大麦C端场景下SVG应用
1.SVG介绍
可伸缩矢量图形(ScalableVectorGraphics),用来定义用于网络的基于矢量的图形,使用XML格式定义图形,图像在放大或改变尺寸的情况下其图形质量不会有所损失,是万维网联盟的标准,DOM和XSL之类的W3C标准是一个整体,不失真,兼容现有图片能力前提还支持矢量(浏览器兼容情况),通过浏览器很早版本支持情况在主流浏览器都支持,SVG提供的功能集涵盖了嵌套转换、裁剪路径、Alpha通道、滤镜效果等能力,它还具备了传统图片没有的矢量功能,在任何高清设备都很高清。
图1SVG与其他格式图片比较
2.SVGKit使用
浏览器默认就支持SVG渲染,属于XML-Dom家族系列,但是在移动端上并没有做原生支持,还是按照XML进行的读取,支持的开源库也不多,在IOS上,目前OC版本SvgKit还不错,官方Github也在继续维护,虽然更新较慢,通过几次patch提交PR还是很快merge的,一些通用属性和控件支持的不够完善,需要进行定制开发,swift版本的macaw也不错,在动画效果上更加酷炫,目前也正在做swift效果迁移到OC中,渲染流程如下:
图2SVGKit渲染加载流程图
1)SVGKit有哪些标签?
circle=SVGCircleElement;【圆形】clipPath=SVGClipPathElement;【层叠路径】description=SVGDescriptionElement;【描述】ellipse=SVGEllipseElement;【椭圆】g=SVGGElement;【容器标签】line=SVGLineElement;【直线】path=SVGPathElement;【路径】polygon=SVGPolygonElement;【多角形】polyline=SVGPolylineElement;【多边形】rect=SVGRectElement;【矩形】svg=SVGSVGElement;【SVG容器标签】switch=SVGSwitchElement;【选择】text=SVGTextElement;【文本】textArea=TinySVGTextAreaElement;【区域文本】title=SVGTitleElement;【标题】2)扩展基于三端统一SVG标签和属性
图3SVG标签属性扩展大图
3)SVG标签在SVGKit中渲染流程
a)SVGKit核心渲染原理分析
视图SVGKFastImageView.m加载到窗口显示核心中心处理类,主要加载SVG文件资源文件
类:SVGKimage:NSObjectSVGKParseResult*parsedSVG=[parserparseSynchronously];//解析SVGKImage*finalImage=[[SVGKImagealloc]initWithParsedSVG:parsedSVGfromSource:source];
b)分析:
解析SVG-到合成ViewLayer初始化SVGKSourcesourcesvg资源实例初始化SVGSVGElement->DomTree初始化SVGDocument->DomDocumentCALayerTree最终合成的Layer树SVGKParser*parser=[SVGKParsernewParserWithDefaultSVGKParserExtensions: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映射到iOSlayer绘制3.生成UILayer:-(CALayer*)newCALayerTreeCALayer*newLayerTree=[selfnewLayerWithElement:self.DOMTree];CALayersublayer=[selfnewLayerWithElement:(SVGElement)child];[newLayerTreeaddlayer.Sublayer][elementlayoutLayer:layer];[layersetNeedsDisplay];
4)SVGKit分析总结
[NSDatadataContentWithBase64Str:str]
3.基于CSS着色能力
1)为什么用CSS着?
SVG虽然是绘制图形,原理如同HTML,是给每一个标签设置一个单独style好还是通过CSSId/class映射好呢,这个思路和HTML处理STYLE样式是一样的,便于更改和维护,在性能上也更好,同时增加了important属性,可以更好的配置样式,可做到运营侧根据样式style下发方式达到更改SVG图效果,可以做到更多活动效果及个性化需求。
2)SVG-CSS着色渲染过程
SVG标签基于CSS样式快速应用,通过遍历DomTree,找到对应的Node节点,在给node节点设置id或者class,然后局部刷新Tree父节点,实现换色,细节流程如下
图4CSS着色原理与时序图
3)大麦端选座渲染效果
图5CSS着色渲染效果
4)CSS着色原理总结及性能比较如何确定属性使用的是CSS颜色还是自带style属性?
当SVG在解析生成DomTree后,我们可以根据CSSStyle样式存储的CSS样式,给Node标签设置id及class,当更改nodeList后,相当于树结构进行了修改,在绘制时候查找属性会根据优先策略id>class>进行查找进行属性赋值,我们根据CSS属性!important来设置最高优先级,这样就避免了此问题。
端侧渲染流程如下,左侧:是基于node遍历后修改,右侧是修改id/class方式【推荐】。性能比对:为了兼容W3C标准,端上增加了CSS特殊属性important。
图6SVG-Codec总体性能提升对比
4.SVG约束DTD
1)背景介绍
当SVG生产端在制作SVG图,可能会用到Adobe等软件,有很多复杂属性及层叠,可能会产生复杂XML格式,这样在渲染过程中会造成大量遍历,影响性能,也有一些特殊属性,端上并没有支持,例如滤镜、动画,这样,我们就需要有一种约束来校验生产和渲染SVG能够一致。
2)文档类型定义
(DTD)可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。
3)定义一个名为note的DTD
一个DTD的内容示例:
其中:
!ELEMENTnote定义note元素有四个元素:"to、from、heading、body"
!ELEMENTto定义to元素为"#PCDATA"类型。
PCDATA的意思是被解析的字符数据(parsedcharacterdata)。可想象为XML元素的开始标签与结束标签之间的文本,PCDATA是会被解析器解析的文本。这些文本将被解析器检查以及标记,文本中的标签会被当作标记来处理,而实体会被展开,不过,被解析的字符数据不应当包含任何&、<或者>字符;需要使用&、<以及>实体来分别替换它们。
它表示在和之间可以插入字符或者子标签,CDATA的意思是字符数据(characterdata,CDATA是不会被解析器解析的文本。在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。
5)如何校验
其中,必须设置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万,浏览器渲染显然不行。
图7选座性能优化-预加载
图8端选座性能交互图
6.SVG场馆彩虹图实现
1)SVG彩虹图介绍:
在售票选座业务中,需要为用户显示场馆图(SVG格式),给用户一个场馆的整体印象,同时方便用户选择场馆的看台,进而展示看台座位进行选座。但是,在使用彩虹图展示场馆图之前,场馆图的看台区域仅展示看台当前可售座位中的最高票价对应的颜色进行展示,如下图所示:
图9非彩虹场馆图
每个看台都是单一颜色的,不能反映出当前看台中可售的座位的价格分布情况,很容易迷惑用户,造成每个看台只有一种票价的印象,同时不方便用户快速定位他的目标价位所在的区域。为了准确的反映出场馆每个看台的价格情况,需要将看台中所售的所有的座位的价格展示出来,因此,采用彩虹图的形式。目标效果如下所示:
图10彩虹场馆图
每个区域的所有的座位价格以彩虹的形式显示出来,相比以前的只显示最高票价的颜色,彩虹图可以清晰的展示每个区域中座位的价格情况。
2)总体思路:在每个看台区域中以彩虹图形式展示多个颜色,就需要将每个看台区域进行划分,放弃之前用一个这类的绘制标签来展示一个看台区域,一个区域内应该包含多个排,对每个排按照座位价格进行着色,进而对每个看台进行同样的处理,总体上形成彩虹样式展示。因此,一个看台区域,应该是多个svg标签组合而成的。如图:
图11SVG文件说明
对SVG底图进行改造,将老的一个SVG标签代表一个看台区域的形式,改造成每个看台区域由标签进行包裹的多个标签的组合。
为了方便降级,处理不显示彩虹图的业务需求,同时约定标签下的第一个标签,表示整个区域,同时不再解析渲染后面的排信息。
3)算法生成彩虹图方案a)介绍:
彩虹图生成算法主要是通过座位的分布和座位的票档圈出一个看台中相同票档座位的范围,然后生成一个path路径。将这些path路径加入到svg底图中去并且和相应的票档绑定,就能实现一个区域多种颜色的效果。
b)步骤如下:
对看台中所有排和座位进行分组排序;
②计算看台方向;
③获取同种颜色座位边界;
④计算色块的方向;
⑤获取色块的路径;
⑥生成看台的彩虹图效果;
⑦遍历所有看台生成完整的彩虹图。首先将某个看台所有座位按排分组,然后将排按排号从小到大进行排序。数据结构如下:
图12看台编号与座位号示例
座位数据是必备的基础数据,后续一切的计算都依赖于座位数据。通过第一排和第二排座位的相对位置算出看台的方向。
比如:拿到1排1号和2排1号座位的坐标,从2排1号向1排1号画一条射线,这个射线的角度就当做看台的方向。
后期如果在生产SVG的时候将舞台位置标记出来的话就可以利用舞台来确定看台方向。
图13看台方向-1
每个看台的座位分布可以分成两种,一排一种价格和一排多种价格。其中一排一种价格的情况就以同色最后一排为边界。如下图绿色的线。一排多种价格的情况就需要把每一排不同颜色转换处的那两个点记录下来连成边界线。如下图红色和黄色的线。
图14看台方向-2
红黄绿三条线所在的座位就是我们需要的色块边界。拿到色块边界座位数组之后还需要知道色块的角度。一排同色的色块方向就直接使用看台
的方向。一排多色的色块方向计算方法如下:
图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);
④根据色块方向获取色块路径的几个关键点:A1A2A3(图2);
⑤将几个关键点和座位边界所有点连接起来生成一个闭合的路径(图3橙色线框)。
图16确定颜色区域-2
拿到所有色块路径后就可以将色块填充对应的颜色并且按顺序叠加到看台上形成彩虹图效果。
色块的叠加方式如下:色块的生成顺序是path1->path2->path3->path4,然后倒序叠加到看台上path4->path3->path2->path1。最后就是遍历所有的看台,生成一张完整的彩虹图。
图17确定颜色区域-3
总结
本文主要讲解了大麦核心链路选座SVG应用,并结合实际场景做了一些创新尝试,包括:丰富SVG应用的业务场景、SVG标签属性及扩展、CSS着色、渲染性能优化等,目的是让端解析接近浏览器解析效果,并提供更好的端选座性能体验。