# 基於 Firebase 的跨裝置即時同步系統實作
## 前言
這個專案是要實現: 在管理員端按下按鈕,會觸發視覺特效於**所有**連線手機的畫面。如圖:
1. 管理員進入 `https://[ngrok]/?role=admin`

2. 一般用戶則進入 `https://[ngrok]`,會有打字機效果跑馬燈顯示祝賀訊息。

4. 當管理員點擊 **放煙火** 則所有人的手機會有視覺特效:

[詳細專案文件可以點我](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

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 | 禁止商業用途 | 轉載標記出處 | 改編作品必須在相同條款下分享。