作者| 阿裡文娛技術專家 莫那、阿裡文娛進階工程師 土嚎
一、背景
1. 網絡售票需要畫座:購票所見即所得
大麥主要業務是票務,包括演唱會、體育賽事、音樂節等,品類繁多。賣票就要畫場館、 畫座位,大家都在網上買過電影票,這不難了解。雖然可以拿電影售票做類比,但底層難度差 異很大。沒有 10W 座的電影院,卻有 10W 座的演唱會,而且演出/體育類場館變化多,座位不固定,場景非常複雜,想随心所欲畫出 10W 座的場館,挑戰相當大。
2. 大麥繪座演進:從示意圖到實際場景圖
大麥以前的繪座系統,是安裝版的程式,畫座位隻能一個看台一個看台的畫,看台之間完 全無關聯,畫出來幾乎每個看台都長一個模樣,座位隻有相對位置的示意圖,沒有角度、距離, 更談不上精确定位。

大麥網從 2017 年,開始設計新版繪座系統。這裡沒有修補,沒有重構,新版繪座完全重來, 連技術棧也由.NET 換成了 Java,由 C/S 換成了 B/S。
新繪座以 SVG 矢量圖為核心,通過 Canvas 進行繪制,在演進的過程中攻克了大量的性能 卡點和技術難點,最終打造成型,堪以重任。
二、新繪座: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 id="myCanvas" width="200" height="100" style="border:1px solid #000000;">
</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)互動+接口優化,進入秒開時代 首先要解決接口慢的問題,解決效率低的一大法寶:化整為零。
從一次 load 一個場館的資料,改成一次 load 幾個看台的資料。服務端資料随着前端視口(頁 面可視範圍)的變化,逐漸加載,類似地圖常用的“拉框查詢”。前端互動也從全加載,改為按 視口取資料。僅此一項優化,幾萬座大場館的系統響應速度,立刻由幾分鐘,降到了 1~2s,小 場館更是瞬時打開,系統好用了不少。
這裡面最重要的一個技術點,就是視口計算,原理如下:
前端首先擷取到螢幕視口在 Canvas 畫布上的坐标,然後和看台的外接矩形進行碰撞檢測, 兩個矩形一旦相交,就說明該看台已暴露在視口之内,于是就加載該看台的資料。
從接口優化開始,新繪座逐漸走向成熟。
2)合并座位矩陣,自由變形 座位自由變形包括傾斜、錯位、排距、座距、旋轉、弧度等多種操作。除了弧度變形,其它基本上是一些數學上的坐标計算,我們不贅述,這裡重點說一下弧度變形。
新弧度變形,運用貝塞爾二階曲線原理,根據使用者的資料輸入,計算出相應的貝塞爾曲線, 再把每排座位,均勻排列到曲線上。下面是貝塞爾二階公式:
注釋:P0、P2 為一排座位的左右端點(一排的第一個座位和最後一個座位) 看似套公式就可以搞定,非常簡單的樣子。但是這裡有一個難點:從圖中可以看出,t 為比例值,處線上段 P0P2 不同的比例,所在的弧度位置也是不一樣的。
如果所有的座位都在 P0P2 線段上,很好算,但是如果座位之前就是一條弧線呢?中間所 有的座位都不在P0P2 線段上,要怎麼算出中間座位的每個比例?
我們通過弧線上的每個座位,做一條 P0P2 線段的垂線,垂線與線段 P0P2 的交點,就是這 些座位所在這一排的原始位置,計算出這些原始位置的坐标,根據這些原始位置,就可以算出 中間所有座位的比例了。
這樣,弧度變形問題就通過貝塞爾二階曲線完美解決。
3)列印順序計算 “列印順序”是個什麼鬼?
這得從大麥的業務特點說起,主辦有時候會批量出票并将票配發給相關人群,有時整個看 台一起列印。在配票的時候,需要按照座位的實體位置關系排序,避免座位沒挨着、“2 個情侶” 被“拆散”的情況發生。舉個例子:下圖中,主辦期望列印票的順序是“5-3-1-2-4-6”,而不是 “1-2-3-4-5-6”,這樣他們就可以按列印順序配票,而不用擔心兩張票不挨着。那麼,在繪座過程 中,我們就要計算出座位的順序,看似簡單,實作起來有難度很大,原因隻有一個,場館形狀各異,座位排列多樣。
如果說,上圖還能按照座位 Y 坐标對比進行排序,那麼下面的幾個情形,就不那麼好處理了:
列印問題,我們通過場景彙總,對場館進行分區,最終找到了排序的規律,得以解決。列印問題技術方案原理:
第 1 步:将場館分成 8 個象限,象限内的座位,已辨別出該如何排序(辨別出了應該對比X 坐标還是 Y 坐标來進行排序);
第 2 步:每一組座位矩陣,取出首排,求首尾座位連線的斜率,然後根據斜率将座位矩陣 劃分到對應象限;
第 3 步:按照對應象限的排序辨別,對比座位的 X 坐标(或 Y 坐标),進行座位排序。
4)小彩蛋之“沙發、角度”
效率、變形和列印 3 個主要問題根解之後,随之出現了大量的産品優化需求,開始着眼于細微之處,小沙發和座位角度就是 2 個典型的功能。這兩個功能雖然難度不大,但卻在體驗上 有了一大步的提升。
5)小彩蛋之“撤回” 經過不斷優化和添磚加瓦,大麥的繪座系統,越來越像一款專業的繪圖工具。好的繪圖工具一定需要“前進&撤回”功能。
新繪座系統的撤回功能實作原理:設計一個“曆史資料”數組,數組裡的每個元素,記錄 一個操作步驟對應的被編輯座位資料以及座位位置資訊,回退時,找到對應操作步驟的數組元 素,重繪座位位置,這樣就回退了整個操作。因為無論座位相對位置如何變形,本質上,其實 都是坐标資料的改變,通過記錄和重繪曆史坐标資訊,就達到了撤回操作的目的。
三、在正确的路上繼續前行
到目前為止,新繪座系統已能夠承接國内外任何大型場館的繪座工程,各種細節的優化也 日臻完善,效率大幅提升。但産品和技術同學的努力,并沒有終止,而是在正确的道路上,繼續前行。
以下簡單列舉幾個很實用的功能,供大家參考:
1. 區域編輯
自由繪制矩形、圓形、多邊形等各種形狀,并自由變形;
2. 一鍵自動變形
全選看台内的座位,點選“一鍵變形按鈕”,座位瞬間适應看台形狀,自動排列;
3. 座位複制、鏡像
區使用者可以自由複制選中座位,并且支援鏡像、翻轉等多種複制模式,排号、座位号根據 設定自動處理;
4. 一鍵朝向舞台
使用者選中一個看台的資料,點選“一鍵朝向舞台”,系統會自動計算舞台方向和座位角度, 瞬間将整個看台座位“擺正”。