# 基於 Firebase 的跨裝置即時同步系統實作 ## 前言 這個專案是要實現: 在管理員端按下按鈕,會觸發視覺特效於**所有**連線手機的畫面。如圖: 1. 管理員進入 `https://[ngrok]/?role=admin` ![image](https://hackmd.io/_uploads/Byy23hcdWe.png) 2. 一般用戶則進入 `https://[ngrok]`,會有打字機效果跑馬燈顯示祝賀訊息。 ![image](https://hackmd.io/_uploads/S1DXeacdbx.png) 4. 當管理員點擊 **放煙火** 則所有人的手機會有視覺特效: ![image](https://hackmd.io/_uploads/SkZcgaq_-e.png) [詳細專案文件可以點我](https://hyc.eshachem.com/program/%e5%9f%ba%e6%96%bc-firebase-%e7%9a%84%e8%b7%a8%e8%a3%9d%e7%bd%ae%e5%8d%b3%e6%99%82%e5%90%8c%e6%ad%a5%e7%b3%bb%e7%b5%b1/) # 權限控制 1. 不用寫兩個頁面,可以直接透過`URLSearchParams`這個函式獲取連結中的訊息。 2. 所以我先透過useEffect抓取現在的身分,如果`role=admin`則useState可以設身分為true。 3. 身分true則顯示"放煙火"按鈕。 4. 否則顯示跑馬燈,[跑馬燈可參考我](https://hyc.eshachem.com/program/%E7%94%A8react%E8%88%87scss%E5%AF%A6%E7%8F%BE%E6%89%93%E5%AD%97%E6%A9%9F%E6%95%88%E6%9E%9C/) 5. 管理員點擊放煙火後會觸發`handleAdminClick` ```typescript= function App() { const [isAdmin, setIsAdmin] = useState(false); const TXTS = ["新年快樂!", "恭喜發財!", "萬事如意!"];// 跑馬燈訊息 useEffect(() => { const params = new URLSearchParams(window.location.search); if (params.get('role') === 'admin') setIsAdmin(true); // 身分驗證 }, []); const handleAdminClick = () => { }; return ( <div className="cny-container"> <div className="cny-card"> <h1 className="lucky-font">福</h1> <div className="status-text"> {isAdmin ? ( <> <p>【管理員模式】</p> <button className="admin-btn" onClick={handleAdminClick}> 放煙火! </button> </> ) : ( <TypeWriter texts={TXTS} /> )} </div> </div> </div> ); } export default App; ``` :::info 同步放煙火的邏輯是: 1. 建立一個雲端資料庫。並建立一個欄位。 2. 所有連線者(包含管理者)監聽這個雲端資料庫中的這個欄位。 3. 如果這個欄位的狀態改變了,就放煙火! ::: 所以在handleAdminClick中我們要實現的是: 改變資料庫中欄位的資料。 所以在這之前,我們要先創建資料庫。 # FireBase :::warning 詳細 FireBase 其專屬語法使用說明可參考[官方文件](https://firebase.google.com/docs/database/web/read-and-write?hl=zh-tw) ::: 1. 搜尋google fireBase。 2. 創建專案後進入RealTime DataBase ![image](https://hackmd.io/_uploads/HkeR4TqObg.png) 3. 會有一串連結,複製下來後貼上程式碼如下: ```typescript= import { getDatabase, ref, onValue, set } from 'firebase/database'; const firebaseConfig = { databaseURL: "https://[Your Project].firebaseio.com/" }; const app = initializeApp(firebaseConfig); const db = getDatabase(app); ``` :::success getDatabase 專門負責「即時數據同步」。執行後,變數 db 就變成了一個遙控器,可以用來 set(修改雲端資料)或 onValue(監聽雲端變化)。 ::: # handleAdminClick 考慮以下程式碼: ```typescript! const handleAdminClick = () => { const signalRef = ref(db, 'cny_signal'); // 更新狀態觸發全員特效 set(signalRef, { status: 'BOOM', ts: Date.now() }); // 1秒後重置,讓下一次點擊仍有效 setTimeout(() => set(signalRef, { status: 'IDLE' }), 1000); }; ``` 這裡的ref是來自`firebase/database`的,功能與useRef大同小異。 `status: 'BOOM':`把雲端的狀態從「等待中(IDLE)」改成「爆炸(BOOM)」。onValue 在監聽。一旦雲端的這個值變成了 BOOM,所有正在看網頁的使用者都會瞬間收到通知,然後同時噴發彩帶。 `ts: Date.now():`這是「時間戳記」。因為Firebase 只有在資料「發生變化」時才會通知大家。如果連續按兩次按鈕狀態都是 BOOM,沒加時間的話 Firebase 可能會覺得「沒變化」而不觸發第二次。加了時間,每一毫秒的值都不一樣,確保每次點擊都是強迫更新!! > 所以接下來要來完成onValue的監聽 # useEffect 1. 使用套件中的onValue來完成監聽 2. 如果監聽到`data?.status === 'BOOM'`,就觸發triggerSurprise。 完整的useEffect長這樣: ```typescript= useEffect(() => { const params = new URLSearchParams(window.location.search); if (params.get('role') === 'admin') setIsAdmin(true); // 監聽 Firebase 訊號 const triggerRef = ref(db, 'cny_signal'); return onValue(triggerRef, (snapshot) => { const data = snapshot.val(); if (data?.status === 'BOOM') { triggerSurprise('恭喜發財!新春愉快!🧧'); } }); }, []); ``` # triggerSurprise 噴彩帶的特效! ```typescript= const triggerSurprise = (msg: string) => { // 震動手機 (如果支援) if (navigator.vibrate) navigator.vibrate([200, 100, 500]); // 噴彩帶 confetti({ particleCount: 150, spread: 70, origin: { y: 0.6 }, zIndex: 9999 // 確保在最上層 }); // 彈窗 Swal.fire({ title: msg, background: '#d32f2f', color: '#fff', confirmButtonText: '收下紅包' }); }; ``` 到這裡就大功告成拉!! # 完整程式碼 https://github.com/Chen11111112/NewYearFireworks.git --- :::info 趁機宣傳一下我自己的個人網站跟Youtube頻道 !! **[個人網站](https://hyc.eshachem.com/) | [Youtube頻道](https://www.youtube.com/@Hy.C)** ::: @2026 Hy.C 陳毓 > Copyright ©Hy.C 陳毓 CC BY-NC-SA 4.0 | 禁止商業用途 | 轉載標記出處 | 改編作品必須在相同條款下分享。