自己婚宴自己搞,婚宴彈幕系統

前言

婚宴結束後已經休息一陣子了,除了準備挪威的行程外,擠出些時間寫個婚宴彈幕系統的心得,以免最後因拖延症搞得無疾而終。

會觸發這場婚宴的原因,主要是因為在 2017/04/28 聚餐時,不曉得話題什麼時候被帶到極光去,而另一半提到想看極光時,有學長提到要看極光最好趕快去,因為近年來已經進到衰退期,迫使我們……回家後就開始查相關資訊,而在六天後已經決定去挪威,且開始下訂機票、郵輪、住宿等,決定在今年農曆過年時去一趟挪威旅遊。決定蜜月時間後,即開始挑場地,最後選上老新台菜,並將時間敲定於 2018/01/06。

在這段時間不時的需要去處理婚宴相關的大小事,但就是沒去處理婚宴當天影片要呈現什麼內容,直到約 11 月底時我們在巴哈動畫瘋上看了一部網路評論部分說很好看的遊戲改編動畫作品,而我們看了一致認同非常粗糙,最後每集都是開著彈幕才有辦法看下去(直接從懸疑片變成搞笑片),後來才有了「很多人都會在婚宴上放影片輪播,不如我們把影片加上彈幕系統讓畫面更有趣吧!」的想法出來,另外因為婚宴的座位安排遲遲無法決定,所以也決定寫個線上查詢座位的功能來讓座位可以隨狀態調整(例如臨時有誰要來爆位置,有誰不來使得一桌少於幾人),而最後拖到 2017/12/27 才開了 git repo 出來開始配置專案,開始撰寫彈幕系統跟帶位系統。所幸還有周末遇到跨年跟婚宴前兩天請假,有幾天可以專心寫程式不用擔心寫到太晚需要中斷思緒的問題,這幾天都是做到早上五六點才睡,活像個趕學期專題的大學生。

彈幕系統主要希望是賓客在看影片播放時,也能跟其他人互動交流一下,可能是吐個槽、也可能是交個朋友,就像在看動畫瘋、niconico 一樣,有時可以看到某些留言是有相關性的,也有些留言可能根本就離題,看了也是很有趣,也希望可以讓賓客們找點事做,不會因為影片輪播過一次後就沒有新鮮感,因為留言的內容只會越來越多,也可能會出現其他沒看過的訊息;而對於我們來說,我們知道自己在現場是沒什麼機會看到的,所以事先規劃了資料儲存來讓我們事後自己找時間看,除了部分是當天利用寫好的後台來看留言並隱藏不當留言外,配上影片的完整版本也是婚宴結束後才看的。

看過完整版本後也試著將它錄下來,可惜電腦不夠力,錄下來似乎有點卡,這是隱藏掉部分留言後的最後版本,也就是現場看到的版本。

影片截圖
影片截圖

同時也順便看了一下若將隱藏的留言通通呈現後會長什麼樣子,會發現多了一些大學朋友們現場測試 SQL injection 跟 XSS attack 的一些訊息,這些人給不給活路啊!不過還好沒出什麼問題就是 :P(雖然這類型留言還是被我們隱藏起來了)

未隱藏部分留言的影片截圖

實作細節

這個系統架設於 Azure 上,也是第一次接觸 Azure,使用過程上非常輕鬆,沒有過多的陣痛期,主要用的資源是 App Service 跟 Azure Cosmos DB:App Service 快速建立起 Node.js 環境,讓我可以專注在 Node.js 的程式上面,且利用 GitHub 進行 Continuous Deployment,也省去一些手動上傳的動作;Azure Cosmos DB 則是讓我輕鬆建立 mongoDB 來儲存資料。

伺服器網頁框架採用 Express.js,手機發送留言跟影片彈幕功能則是用 socket.io 來處理 websocket 的部分,讓訊息可以即時呈現;影片的彈幕最小單位是秒,我們決定讓它出現於畫面底部且共十個軌道,其位置是利用 Fisher–Yates shuffle 來進行隨機安排,因此每一次播到同樣秒數時,每個留言的位置都是隨機的。除了十軌外,留言移動的速度也是隨機十種,但不做十種 shuffle 而是直接隨機,因此同一秒出現的留言可能會有快有慢,也可能有相等的;而若同一秒內超過十個留言,則是每十個留言做一次位置的 shuffle,最後同時丟出,其中就會出現留言重疊的事件,而留言多到重疊我們也認為這是彈幕的一種特色,且因為時間緊迫,因此就沒特別處理了,只能期望重疊的留言因速度不同的而能分辨各個留言的內容。

而留言系統我們曾考慮過到底該用黑名單還白名單機制來讓人留言,以及是否該綁定 Facebook 第三方登入的問題:黑白名單主要差在是否留言後須先通過審核才會出現,或是出現後再遭隱藏的問題,最後我們選擇使用黑名單機制,認為我們賓客都是很乖的,如果亂留言一定是交到壞朋友才這樣的;綁定 Facbook 登入的問題,以我們自己的觀點來看,若是需要綁定 Facebook 登入來驗證姓名的話,那我的參與意願就會降低,因此最後選擇直接輸入自已的姓名即可,即便害羞要打暱稱也不是不行。而在這點上我們徹底的錯了。原以為某個最有可能亂來的人因為出差去美國所以不用擔心太多的我們真是太天真了。好在防人之心不可無,必要的防護我還是有處理的,不然影片彈幕可能就被 XSS attack 玩壞了。當天才知道原來不是存在幾個亂留言的壞朋友,而是某桌整桌都是壞朋友!除了各種攻擊外,還出現各種不可能出現的姓名留言,例如我自己的名字,還有系上教授的名字,雖然很好笑,但也太 troll 了吧 XDD。好在有寫一個陽春的後台系統,可以即時顯示/隱藏留言及開啟/關閉留言功能,如下圖。

手機後台截圖
手機後台截圖

影片彈幕系統的部分,由於影片是放在 local 的電腦上,而又須連到網路上拿取後臺資料,因此利用 Google Chrome kiosk mode 來呈現,好在曾經做過研究,就直接拿以前的筆記稍微改一下複製貼上就沒事了。

1
chrome.exe "file:///C:/dist/index.html" --user-data-dir="C:/Chrome dev session" --disable-web-security --test-type --disable-translate --kiosk

手機前端網頁部分其實是花最多時間的,主要是 UI 該怎麼呈現也是改了又改改了又改,又再一次體會了下面這張圖的心情。同時也試著使用 History API 等未使用過的功能。

Sums up my new job as a graphic designer.

另外帶位系統是不是多餘的,有親戚知道這個曾問過如果手機沒網路或不會用的人該怎麼辦,回答是「本婚宴不歡迎非親戚與非現代人入場」。因為我們確定除親戚外,其他人平常都會使用到這部分才這樣做,且許多人可能都有雙重身分,例如是大學同學也是社團同學,到底該坐哪桌的問題。且婚宴前一天都還有人臨時想來/不能來,此時我們要動態調整位置時就變得很方便!

可改善的部分

  • 未考慮到使用者可能會輸入 emoji,還好 emoji 能正常顯示,只是可能會跟使用者心中的樣式長得不一樣。
  • 留言顏色只有白色,其實有想過更多顏色也許不錯,實作也不難,但後來又覺得會不會太亂就先作罷,看到有留言提到「需要能更換顏色,下班前給我」時覺得沒做好像有點可惜?

總結

寫的過程雖累但有趣,算是累得值得的一件事!
最後再次感謝參與婚宴的所有人,沒有你們的話,這個系統就不會出現。(重點誤)

利用 canvas 於 Chrome 實作 iOS 模糊背景效果

iOS blurry overlay
這次要處理的就是上面的背景模糊背景效果,雖 iOS7 在某些版面上已經有了,但在瀏覽器上要做到同樣的事通常都要疊兩張一樣的圖再做 filter 處理,若今天需要模糊的是比圖片複雜的,例如影片、包含其他動態元件的 html 等就會變得不好處理。
在 Safari 上可以使用 -webkit-backdrop-filter: blur(5px); CSS 屬性來處理,且可以根據上層的 overflow:hidden; 來改變模糊的區域範圍;而在 Google Chrome 上,雖然可以透過 enable-experimental-web-platform-features 來開啟 backdrop-filter: blur(5px); 功能,但在配合其他 CSS 屬性時很容易出現 bug。

backdrop-filter 的實作範例:Chrome 需開啟 enable-experimental-web-platform-features 才能看到結果
Safari 下正常,而 Chrome 也正常,但我需要圓弧邊框:JSFiddle
Safari 下正常,而 Chrome 下若上層有 overflow: hidden; 時則完全無效:JSFiddle

網路上能找到許多解法,像是放兩張圖或放兩個影片,前景的圖層加上 filter:blur 即可,但最後我選擇使用 canvas 來實作看看。

背景是影片的狀況:

不過 canvas 解法反而不適用於 Safari,因為 Safari 不支援 canvas filter…XD

參考資料:

  1. CanvasRenderingContext2D.filter - Web APIs | MDN

HTML 中多個可移動視窗互相覆蓋的處理方法

雖說是視窗,但其實是個元件,不過稱視窗好像比較直觀些。
總之先看已經用 javascript 完成可移動的多個視窗功能,在沒有做任何處理的情況下,利用滑鼠去移動分別標有數字的方塊,會發現數字高的永遠在上層:

我想到的解決方法有兩種:

  1. 改變 DOM 的順序
  2. 改變 z-index

改變 z-index


缺點:z-index 可能會超過其他元件的 z-index,因此最好讓移動視窗的 z-index 固定在一個區間內循環。(z-index 值的區間為一個 int)

改變 DOM 的順序


缺點:若內容有像卷軸一樣無法用 CSS 控制的部分,在改變 DOM 的順序後將不會保留原先狀態,如下範例的數字 1 方塊。


解決方法:先把所有額外的狀態記起來,移動過順序後再將這些狀態指定回去。

最後我選擇了改變 z-index 的方法,因為我的移動視窗內可能會有多個卷軸,若要一一紀錄狀態實在麻煩,而且實測兩個方法的重繪效率上,z-index 也是略勝一籌。

利用 windows batch 自動初始化、檢查 Google Chrome 開啟狀態

延續上一篇利用 Google Chrome 作為 kiosk 應用時的設定項目所提到的,這個最終要應用在 batch 上,來達到在 kiosk 應用上能自動開機便能自動初始化、自動檢查 Chrome 是否因為不明原因被關閉了,減少作業系統曝光的情況,否則哪天說不定就會被拍照下來留念。

Windows 出來呼吸了:http://t17.techbang.com/topics/20686-tv-advertising-is-running-windows-7-11

batch 檔內容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
:: detectChrome.bat
@echo off
:: 初始化
echo Initialize...
:: 更改資料夾到正確位置
cd C:\

:: 檢查網路狀態
echo Waiting for Interent connection...
:checkInternet
ping www.google.com -n 1 -w 10000 > nul
IF errorlevel 1 GOTO checkInternet

:: 這裡可放入自己想放的 code
::node update.js

:: 進入迴圈
:start
:: 若 processEnd.tmp 則跳脫迴圈結束程式
IF EXIST processEnd.tmp (
echo processEnd.tmp detected! End the process.
GOTO end
)

tasklist /FI "IMAGENAME eq chrome.exe" /FO TABLE > process.log
FOR /F %%c in ('find /v /c "" ^< process.log') DO set lineCount=%%c
:: 刪除暫存檔案 process.log
DEL process.log
:: 若 process 存在,則 lineCount 會大於 1,不存在則等於 1,因此若大於 1 則跳過執行部分
IF %lineCount% GTR 1 GOTO skip
:: 執行程式
echo Execute Chrome
::"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --user-data-dir="C:/Chrome dev session" --disable-web-security --new-window "file:///C:/dist/index.html" --test-type --kiosk
start /WAIT /MAX chrome.exe --user-data-dir="C:/Chrome dev session" --disable-web-security --new-window "file:///C:/dist/index.html" --test-type --disable-translate --kiosk
:skip

REM 等待一段時間後再次檢查(秒)
timeout /t 5
GOTO start
:end
DEL processEnd.tmp

主要流程為需要開機後直接執行更新,但我的環境曾遇到開機後網路異常的現象,因此檢查網路是否有沒有通,如果不擔心此問題的話則可考慮把檢查網路狀態那幾行拿掉。接著再執行自己需要的程式,我這邊已有一現成用 node.js 撰寫的程式,因此就讓他執行 node update.js。接著就進入 Chrome 判斷的迴圈了,為避免意外重複開啟 Chrome,因此先判斷了是否有 Chrome 在開啟狀態,若都沒有再開啟。

接著把這隻 batch 檔案丟到「所有程式」的「啟動」中就行了,或是利用「工作排程器」也可以達到同樣效果,唯一不方便的是有需要對電腦做操作時,要關掉這隻程式不是很方便,因此我又另外寫了一隻關閉這隻程式的 code,很懶惰的用了檔案檢查的方式去處理,在 detectChrome.bat 的迴圈中判斷是否有個暫存的檔案 processEnd.tmp,若有的話則不要中止程式。

1
2
3
4
:: end.bat
@echo off
type nul > C:\processEnd.tmp
echo 等待倒數後結束程式

如此一來,不太懂的操作者也可以輕鬆的用 alt+tab, windows+d 等按鍵從 chrome kiosk 切換出去,然後開啟這隻 end.bat,再把 chrome alt+f4 關閉來完整結束這個程式帶來的影響。

bash 寫慣了,要寫 batch 還真不太適應,所幸有朋友的 Batch 溫故/知新 支援省了我不少時間。

參考資料:

  1. Batch 溫故/知新
  2. 不用寫程式,偵測執行檔沒running就再次執行它
  3. Count number of lines in a text file from a windows batch file
  4. 如何在批次檔(Batch)中實現 sleep 命令讓任務暫停執行 n 秒
  5. windows - If greater than batch files - Stack Overflow

利用 Google Chrome 作為 kiosk 應用時的設定項目

紀錄一下利用 Google Chrome 作為 kiosk 時,需要注意的地方:
本文所使用的 Google Chrome 版本:52.0.2743.82 m

鎖定右鍵內容

當觸控啟用時,預設長按螢幕就會觸發滑鼠右鍵的功能表。如同一般網頁防止滑鼠右鍵內容一樣,於 HTML 中加入 Javascript 來防止右鍵清單的跳出。

1
window.addEventListener('contextmenu', function(e) { e.preventDefault(); });

禁止文字被選取

當長按螢幕時會觸發與滑鼠左鍵按住拖曳來選取文字同樣的效果,如下圖中間搜尋視窗:
長按螢幕字串時
若不想被選取,可以利用 CSS 處理,根據自己不想被選的部分作出篩選,下方範例以整份 HTML 當作範例:

1
2
3
html {
-webkit-user-select: none;
}

關閉觸控滑動拖曳到上一頁/下一頁

若有超連結會導到其他頁面時,導向其他頁面後按著螢幕並往左或往右滑動,可能會觸發如手持裝置般的上下頁功能:
當有歷史分頁時,碰觸畫面並往右滑將會回到上一頁
解決方式有兩種:

  1. 利用 CSS 去解決:

    1
    2
    3
    4
    html {
    touch-action: none;
    -webkit-user-drag: none;
    }

    但有缺點!若在 HTML 內容中有需要橫向卷軸的部分,此橫向卷軸部分不可能套用上方 CSS,需再另外對 touch-action-webkit-user-drag 允許橫向操作,而若允許之後,在此橫向卷軸部分拖曳到最頂/底端時再繼續拖曳,依然會觸發道上下頁的功能。

  2. 修改 Chrome flags 內的設定:
    開啟 Chrome 新分頁,在網址列輸入 chrome://flags 並找到 橫向捲動紀錄導覽 Mac, Windows, Linux, Chrome OS, Android,預設為已啟用,把它改成已停用
    更改設定為已停用
    如此一來連 CSS 也不用寫了,缺點就是這個為全域設定,改成停用後任何網站都不能使用,不過既然是 kiosk,改成已停用理應影響不大。


接下來設定皆與命令列(command line)參數有關,使用方法有:

  1. 利用建立捷徑的方式來填參數:
    利用建立捷徑方式填參數
  2. 直接開啟執行(Windows + R)並輸入指令及參數:
    在執行中輸入
  3. 於命令提示字元(cmd)中執行其中一行:

    1
    start chrome
    1
    "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"

開啟固定位址的內容

開啟時直接進入某個網頁。
在參數列加上網址即可,如 start chrome ssk7833.github.io
若是本機上的靜態檔案則須加上 file:///,如 start chrome file:///C:/dist/index.html

運行 kiosk 模式

開啟時直接進入全螢幕模式,且無法利用 F11ESC 來離開全螢幕模式,可以用 ALT + F4CTRL + W 來關閉。
在參數列加上 --kiosk 即可。

關閉詢問「您要翻譯這個網頁嗎?」

您要翻譯這個網頁嗎?
使用 kiosk 模式後,依然可能會因 HTML 撰寫或內文而自動跳出這個訊息,在 kiosk 模式這當然是不想要的。
在參數列加上 --disable-translate 即可。

允許 Chrome 無視跨來源資源共享(CORS)限制

如果 kiosk 的內容是靜態網頁而無後台,又需要利用 AJAX 其他地方拿取資料,很可能會在開發時出現這種錯誤:
XMLHttpRequest cannot load https://www.example.com. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
CORS 錯誤
若不想額外增設後台可在參數列加上 --disable-web-security,但加上後錯誤會依然存在,需配何下方參數才能正常運行。

開啟新的使用者資料夾來放置內容

如同一台電腦支援多個 Chrome 使用者一樣,另外開一個額外的資料夾來放置 user data,使其不會各自汙染。
在參數列加上 --user-data-dir="C:/Chrome dev session" 即可,C:/Chrome dev session 即是接下來這個 Chrome 視窗將會存放資訊的位置,此資料夾無需自行建立,系統會自動幫忙建立。
--disable-web-security 搭配 --user-data-dir="C:/Chrome dev session" 即可無視 CORS 限制,但使用上也須注意是否有其他安全性的疑慮,不過既然都是 kiosk 了,理應不太會有這個問題。

移除上方跳出的警告訊息

前兩項做到後,其實會跳出此訊息:您正在使用不受支援的命令列標識:--disable-web-security。這可能會危及穩定性與安全性。
--disable-web-security 警告
在 kiosk 模式當然不可能放出此行,可以在參數列加上 --test-type 即可。為什麼這幾個不一次講完呢?

TL;DR

上述這幾個的參數組合技已經蠻夠用的,我目前下的指令如下:

1
chrome.exe "file:///C:/dist/index.html" --user-data-dir="C:/Chrome dev session" --disable-web-security --test-type --disable-translate --kiosk

為什麼要用指令呢?因為能撰寫於 batch 中來達到部分的自動化。
不確定某些指令是否可能有潛藏的風險,若有思慮不周的地方也請您分享!

UPDATE: 下一篇出來了:利用 windows batch 自動初始化、檢查 chrome 開啟狀態

參考資料:

  1. Google Chrome browser, how to permanently disable this disturbing toolbar?
  2. Protractor error message “unsupported command-line flag” in Chrome?