10万人的大场馆如何“画座位”?

一、背景

1、网络售票,需要画座:购票所见即所得

大麦主要业务是票务,包括演唱会、体育赛事、音乐节等,品类繁多。卖票就要画场馆、画座位,大家都在网上买过电影票,这不难理解。虽然可以拿电影售票做类比,但底层难度差异很大。没有10W座的电影院,却有10W座的演唱会,而且演出/体育类场馆变化多,挑战相当大。

2、大麦绘座演进:从示意图到实际场景图

大麦以前的绘座系统,是安装版的程序,画座位只能一个看台一个看台地画,看台之间完全无关联,画出来几乎每个看台都长一个模样,座位只有相对位置的示意图,没有角度、距离,更谈不上精确定位。

10万人的大场馆如何“画座位”?

图1:老版绘座页面(已淘汰)

大麦网从2017年,开始设计新版绘座系统。这里没有修补,没有重构,新版绘座完全重来,连技术栈也由.NET换成了Java,由C/S换成了B/S。

新绘座以SVG矢量图为核心,通过Canvas进行绘制,在演进的过程中攻克了大量的性能卡点和技术难点,最终打造成型,堪以重任。

10万人的大场馆如何“画座位”?

10万人的大场馆如何“画座位”?

图2:新绘座页面

二、新绘座:Flash走了,Canvas来了

1、Flash已死,来到路口,别无选择

新绘座已诞生2年多,现在回首,这条路似乎早已注定。

老版绘座和选座是基于Flash的,悲剧的是,Adobe宣称Flash 2020年后,将不再维护,相关技术会在2020年底全部退役,大麦的绘座和选座系统,都*转型。

Flash只是原因之一,看过竞争对手的产品,会发现SVG是条不错的道路,即使没有Flash这一出,大麦网也会朝这个方向迈进。

2、技术选型

1)任何过度使用DOM的应用,都不会快。

经过技术调研,发现国外一些场馆座位绘制,选用的是SVG方案,每个座位都是一个独立的SVG元素。但如果直接把SVG搬到浏览器,无法支持几万座位的场馆,因为浏览器无法支持过多的DOM数量,并且,一旦DOM数量太多,操作一定是低效的,“任何过度使用DOM的应用,都不会太快”。

于是,技术同学想到了Canvas,Canvas是浏览器上的一个画布,无论上面绘制多少元素,对于浏览器而言,都只是一个DOM元素。

对于不了解Canvas的同学,我们可以简单做个说明,Canvas在浏览器上,就是下面一个标签:


在Canvas上绘图,就是使用JS获取Canvas对象,使用封装好的方法进行绘制。Canvas画布上的图形变化,完全通过擦除+重绘的方式展现。

那么新绘座的目标就变得很明确了,我们就是要在Canvas上绘制出想要的场馆座位图,然后以SVG的格式把图形保存起来,用以选座、售票。

2)Canvas也不是银弹:单个Canvas的大小是有限制的,超限之后也会卡顿。

选型初期,技术同学使用Canvas+SVG做了个Demo,模拟了10W座位的渲染,并实现了拖拽、缩放。但真正作为画座组件开发的时候,发现座位达到2W就出现了卡顿,因为Canvas的宽高达到一定的数值,就会出现卡顿。于是,沿着化整为零的思路,技术同学将整个画布,分成了多份Canvas,形成了一个Canvas矩阵,通过对每个Canvas的操作,完美解决了单个Canvas过大引起卡顿的问题。

关于Canvas绘图组件,大家可以在网上搜到很多资料,这里不再赘述。

3、新版绘座上线初期:青苹果

刚上线的新版绘座,就像个青涩的苹果,虽然漂亮,却没那么好吃。

最突出的问题有2个:第1个是变形难用,由于算法比较初级,座位矩阵变形很难满足用户需求;第2个是接口速度慢,打开一个1W座的场馆,好几分钟,超过5W,直接崩溃,根本无法支持。

为什么理想很丰满,结果却差强人意呢?根源在于第一版只重功能,忽略了算法效率。与服务端的接口调用,都是整个场馆级别的,几万座位数据,加上关联的看台、票、以及状态等,一个硕大的数据包在前后端丢来丢去,系统不堪重负,用户受尽折磨。

4、艰苦改进之旅

新绘座上线后,立刻启动了改进优化工程,主要攻克的难关有三点:1. 页面响应时间;2. 座位*组合变形;3. 打印顺序计算。

1)交互+接口优化,进入秒开时代

首先要解决接口慢的问题,解决效率低的一大法宝:化整为零。

从一次load一个场馆的数据,改成一次load几个看台的数据。服务端数据随着前端视口(页面可视范围)的变化,逐渐加载,类似地图常用的“拉框查询”。前端交互也从全加载,改为按视口取数据。仅此一项优化,几万座大场馆的系统响应速度,立刻由几分钟,降到了1~2s,小场馆更是瞬时打开,系统好用了不少。

这里面最重要的一个技术点,就是视口计算,原理如下:

前端首先获取到屏幕视口在Canvas画布上的坐标,然后和看台的外接矩形进行碰撞检测,两个矩形一旦相交,就说明该看台已暴露在视口之内,于是就加载该看台的数据。

从接口优化开始,新绘座逐渐走向成熟。

10万人的大场馆如何“画座位”?

图3:按视口加载原理图

2)合并座位矩阵,*变形

座位*变形包括倾斜、错位、排距、座距、旋转、弧度等多种操作。除了弧度变形,其它基本上是一些数学上的坐标计算,我们不赘述,这里重点说一下弧度变形。

新弧度变形,运用贝塞尔二阶曲线原理,根据用户的数据输入,计算出相应的贝塞尔曲线,再把每排座位,均匀排列到曲线上。下面是贝塞尔二阶公式:

10万人的大场馆如何“画座位”?
图4:贝塞尔曲线示意图

10万人的大场馆如何“画座位”?

注释:P0、P2为一排座位的左右端点(一排的第一个座位和最后一个座位)。

看似套公式就可以搞定,非常简单的样子。但是这里有一个难点:从图中可以看出,t为比例值,处在线段P0P2不同的比例,所在的弧度位置也是不一样的。

如果所有的座位都在P0P2线段上,很好算,但是如果座位之前就是一条弧线呢?

中间所有的座位都不在P0P2线段上,要怎么算出中间座位的每个比例?

我们通过弧线上的每个座位,做一条P0P2线段的垂线,垂线与线段P0P2的交点,就是这些座位所在这一排的原始位置,计算出这些原始位置的坐标,根据这些原始位置,就可以算出中间所有座位的比例了。

这样,弧度变形问题就通过贝塞尔二阶曲线完美解决。

10万人的大场馆如何“画座位”?
图5:弧形座位矩阵贝塞尔曲线变形原理图

10万人的大场馆如何“画座位”?
图6:弧度变形实际操作
10万人的大场馆如何“画座位”?
图7:座位*组合,随意变形

3) 打印顺序计算

“打印顺序”是个什么鬼?

这得从大麦的业务特点说起,主办有时候会批量出票并将票配发给相关人群,有时整个看台一起打印。在配票的时候,需要按照座位的物理位置关系排序,避免座位没挨着、“2个情侣”被“拆散”的情况发生。举个例子:下图中,主办期望打印票的顺序是“5-3-1-2-4-6”,而不是“1-2-3-4-5-6”,这样他们就可以按打印顺序配票,而不用担心两张票不挨着。那么,在绘座过程中,我们就要计算出座位的顺序,看似简单,实现起来有难度很大,原因只有一个,场馆形状各异,座位排列多样。

10万人的大场馆如何“画座位”?
图:8:北京奥体中心的某个看台

如果说,上图还能按照座位Y坐标对比进行排序,那么下面的几个情形,就不那么好处理了:

10万人的大场馆如何“画座位”?
图9:各种特殊的座位排列场景

打印问题,我们通过场景汇总,对场馆进行分区,最终找到了排序的规律,得以解决。打印问题技术方案原理:

第1步:将场馆分成8个象限,象限内的座位,已标识出该如何排序(标识出了应该对比X坐标还是Y坐标来进行排序);

第2步:每一组座位矩阵,取出首排,求首尾座位连线的斜率,然后根据斜率将座位矩阵划分到对应象限;

第3步:按照对应象限的排序标识,对比座位的X坐标(或Y坐标),进行座位排序。

10万人的大场馆如何“画座位”?
图10:座位排序原理图

4)小彩蛋之“沙发、角度”

效率、变形和打印3个主要问题根解之后,随之出现了大量的产品优化需求,开始着眼于细微之处,小沙发和座位角度就是2个典型的功能。这两个功能虽然难度不大,但却在体验上有了一大步的提升。

10万人的大场馆如何“画座位”?
图11:圆点、沙发效果对比

5)小彩蛋之“撤回”

经过不断优化和添砖加瓦,大麦的绘座系统,越来越像一款专业的绘图工具。好的绘图工具一定需要“前进&撤回”功能。

新绘座系统的撤回功能实现原理:设计一个“历史数据”数组,数组里的每个元素,记录一个操作步骤对应的被编辑座位数据以及座位位置信息,回退时,找到对应操作步骤的数组元素,重绘座位位置,这样就回退了整个操作。因为无论座位相对位置如何变形,本质上,其实都是坐标数据的改变,通过记录和重绘历史坐标信息,就达到了撤回操作的目的。

三、 在正确的路上继续前行

到目前为止,新绘座系统已能够承接国内外任何大型场馆的绘座工程,各种细节的优化也日臻完善,效率大幅提升。但产品和技术同学的努力,并没有终止,而是在正确的道路上,继续前行。

以下简单列举几个很实用的功能,供大家参考:

1)区域编辑:*绘制矩形、圆形、多边形等各种形状,并*变形;

2)一键自动变形:全选看台内的座位,点击“一键变形按钮”,座位瞬间适应看台形状,自动排列。

10万人的大场馆如何“画座位”?
图12:一键变形效果图

3)座位复制、镜像:区用户可以*复制选中座位,并且支持镜像、翻转等多种复制模式,排号、座位号根据设置自动处理;

4)一键朝向舞台:用户选中一个看台的数据,点击“一键朝向舞台”,系统会自动计算舞台方向和座位角度,瞬间将整个看台座位“摆正”。

上一篇:2020年的双11,阿里需要什么样的渲染方案?


下一篇:每秒7亿次请求,阿里新一代数据库如何支撑?