起源於interview某家公司出的小作業
實作一個桌機與手機互動的小demo
紀錄其中的過程
起源
公司提供了一個參考影片,影片中的桌機畫面是一個web動畫,以及一個行走中的小角色
影片中的人拿著手機,以橫向翻轉手機時,桌機動畫裡的場景會改變,ex:白天-黑夜
可能也會改變動畫故事的發展
發想
首先survey了手機內含的感測器,主要常見的像是
-
近距感測器(Proximity Sensor)
透過接受到的紅外線強度判斷距離,可以偵測通話時是否貼在耳朵,決定是否關閉螢幕。
-
環境光(光度)感測器(Ambient Light Sensor)
感測環境光線的強度,用來調節手機螢幕亮度
白天提高螢幕亮度;夜晚降低螢幕亮度
提高使用者舒適程度也能延長電池壽命
-
加速度計(Accelerometer)
切換橫直向螢幕或是計步
-
磁感測器(Magnetism)
測量電阻來判斷磁場強度,應用在指南針或是地圖導航
-
陀螺儀感測器(gyroscope)
常看到的應用像是:用手機模擬方向盤的賽車遊戲、槍擊遊戲,GPS導航,拍照時會偵測裝置晃動程度以方便處理出穩定的照片
-
GPS
裝置裡的GPS模組透過衛星的瞬間位置計算距離,用在定位、測速、測量距離。
要讓使用者最能上手操作的變因,首選應該就是裝置的方向
互動內容
既然決定讓使用者透過裝置的方向或是加速度來改變某些事件
下一步就是關於到底要做什麼內容
過去有關的經驗不外乎就是利用裝置當作方向盤的賽車遊戲
模擬出一種身歷其境的體驗
開始想…現實生活中有哪些事情
或有需要搖晃或是控制方向的事物/物體
配合生活經驗,剛好這陣子迷上幸運餅乾籤詩,但幸運餅乾沒有可以模擬的動作
於是決定改類似概念-採用廟裡搖籤抽運勢和籤詩作為方向
素材
決定內容後,上網開始找圖庫找素材找靈感
Flaticon-PPT需要小icon吸睛時,很推! 或是Freepik
剛開始卡在英文關鍵字不容易下,而且目標物又是比較偏東方的東西
fortune paper , temple, lottery 搜尋出來都不是想要的
於是利用google的圖片搜尋
看到一張日本籤筒的小插圖, 根據上面寫的平假名(Omikuji),再用google搜尋
找到一張滿意的圖片,決定用illu重製復刻它
把內容項目分圖層、背景圖畫好後,再輸出成圖檔
Wireframe&Prototype
構思流程大約是
-
桌機進入首頁畫面,一小段animation(背景淡入and籤筒浮現)
-
桌機畫面會提示使用者也用手機裝置登入
- 手機裝置進入同一個網頁後,出現shake的字樣
- server偵測到手機裝置的加速度值改變,觸發註冊事件(事件為跳窗方式顯示籤詩結果)
- 桌機畫面出現抽籤結果
切版
進入到撰寫HTML&CSS的部分
主要用到css-position定位
以及根據呈現方式寫腳本(css-animation-keyframes)
背後大大小小的籤則是使用parallex.js 呈現視差的效果
關於提示訊息或是跳窗的css部分,找了不少在網路上的素材參考(copy-paste…)
推推Free Frontend-整理很多線上範例都很fancy很可愛~(也許比較適合side project用)
Free-Frontend網站選單分三大項:
-
HTML+CSS
再細分html example& css example
比如常需要做的頁面404,500,這個網站也有整理一個主題出來
EX: 500 Error Page HTML Templates
這篇主題中整理的了13種500 error page
底下會附上一些基本資訊
最重要的會有demo和code的連結,以及使用的工具(html/css or javascript)
CSS Code examples的部分,很多都是純css寫出很厲害的效果,很值得看看
-
JAVASRIPT
-
BOOKS
整理很多線上免費電子書,分類齊全Angular,HTML&CSS,Javascript,NodeJS,React
於是頁面拼拼湊湊後逐漸完成
- web開啟的首頁畫面-提示另外再用手機開啟google瀏覽器進入此頁面
- 手機裝置開啟的畫面-提示使用者shake shake!!
- 最後於web畫面顯示結果
Server端&Client端
直到切完版才驚覺這個demo要練的應該是WebSocket(?)
WebSocket
它是網路協定的一種,讓Client可以透過這個協定與Server溝通
只要連結上了,就可以保持互動
(和Http/Https 要一直發送request 收response不一樣)
當device orientation數值達標時,向server傳訊息
sever收到指定訊息後,再統一向所有的client的推送訊息
(即時聊天室也可以用websocket做)
利用node.js 的websocket
Server
建立一個本機的server
// 匯入express和ws套件
const express = require('express');
const SocketServer = require('ws').Server;
//預設開啟3000 Port
const PORT = process.env.PORT || 3000;
//建立express物件,console中顯示"Listening on xxxx PORT
const server = express()
.listen(PORT, () => console.log(`Listening on ${ PORT }`));
// 開啟WebSocket服務
const wss = new SocketServer({
server,
});
//WebSocket 從外部連結時執行
wss.on('connection', ws => {
//有連結時,即開啟brower進入index.html時(不同分頁算不同client)
//console顯示"Client connected"
console.log('Client connected')
//對 message監聽,接收從 Client 發送的訊息data
ws.on('message', data => {
//取得所有連接中的 client
let clients = wss.clients;
//發送data訊息至每個 client
clients.forEach(client => {
client.send(data)
})
})
//連線關閉時 console顯示"Close connected"
ws.on('close', () => {
console.log('Close connected')
})
})
Client
// 在本機run server.js已指定3000 port
//ex:let ws = new WebSocket('ws://localhost:3000')
let ws = new WebSocket('ws://xxxxx:3000');
// connenction start
ws.onopen = () => {
//browser console 顯示"open connection"
console.log('open connection');
// 是否支援DeviceMotionEvent
if (window.DeviceMotionEvent) {
//監聽devicemotion
window.addEventListener('devicemotion', function (event) {
// x,y,z 三個方向的加速度值(m/s^2)
var x = Math.round(event.acceleration.x);
var y = Math.round(event.acceleration.y);
var z = Math.round(event.acceleration.z);
x = Math.abs(x);
y = Math.abs(y);
z = Math.abs(z);
//三個方向的加速度值相乘大於300, 傳送'shake'訊息給server
if ((x * y * z) > 300) {
ws.send('shake');
}
}, true);
} else {
console.log('only support mobile');
}
}
//after close, run something action
ws.onclose = () => {
console.log('close connection');
}
ws.onmessage = event => {
if (event.data == 'shake') {
// Receive the "shake", show the fortunepaper result
let modal = document.getElementById("open-modal");
modal.setAttribute('style', "visibility:visible;opacity:1;pointer-events: auto;");
}
}
目前照這樣的設計,多個Client對到同一個server
只要有一個client觸發了shake, 所有client的modal就會被開啟
11/11更新
如同第一次的demo結果有幾點要改善
-
server要可以對到多組client,每組獨立
-
桌機/筆電 和手機裝置是一組
構想是:
- client端(c1)由browser進入首頁連結
- c1向server進行websocket連線,sever端檢查url是否帶有token,若無產生一亂數作為token
- sever傳回一物件{group:token}給c1 (ex:token=abc)
- c1接收傳回的token值,組合url產生QRcode (ex: url?token=abc)
- 手機裝置掃描QRcode,亦跟server進行websocket連線,成為c2
- server檢查url,回傳c2原有的token (token=abc)
- 故c1和c2的token皆為abc
- client端監聽deviceorientation,加速度值達標時,觸發事件,向server傳送client訊息(要包含token資訊)
- server向所有client推送{shake:token} (ex:shake:abc)
- client端比對server傳來的token是否與自己帶有的token相等
// server.js
//URL是否帶有token
let token = request.url.split("?token=g")[1];
var number = (getRandom(connectLimit));
// 取隨機數
function getRandom(limit) {
return Math.floor(Math.random() * connectLimit) + 1;
}
if (token) {
ws.send(JSON.stringify({
group: token
}));
} else {
ws.send(JSON.stringify({
group: number
}));
}
// client.js
ws.onmessage = event => {
eventObj = JSON.parse(event.data);
//監聽server傳來的group token值
if (Object.keys(eventObj).indexOf('group') !== -1) {
this.token = eventObj.group;
}
// 更換qrcode url
document.getElementById('qrcode-url').src ="http://www.funcode-tech.com/Encoder_Service/img.aspxcustid=1&username=&public&codetype=QR&EClevel=0&data=https://pengpon.github.io/vpon_task?token=g" +this.token
// 監聽server傳來的shake目標
if (Object.keys(eventObj).indexOf('shake') !== (-1) && eventObj.shake == this.token) {
// receive the "shake", show the fortunepaper result
let modal = document.getElementById("open-modal");
modal.setAttribute('style', "visibility:visible;opacity:1;pointer-events: auto;");
}
}
因為多加了掃描QRcode的流程,畫面異動如下
檢討
其實還有很多細節或是畫面需要做:
- 如果同一組token有2個以上的client連線時,該顯示什麼訊息給使用者
- 當與server連線失敗時,該怎麼提醒使用者
- 若一開始使用者是用手機裝置進入首頁(僅有shake字樣),該怎麼提醒 (使用者非照預設的情境走-先用桌機/筆電開啟 再掃QRcode)
再度很感恩有這次玩websocket的經驗!!!
延伸閱讀&參考圖文+code
Using Device Orientation in Html5
An Introduction to the Device Orientation API