調整記分板手機滿版模式並更新說明
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
- 第一分開始後,`設定隊伍` 會改成 `上一步`。
|
- 第一分開始後,`設定隊伍` 會改成 `上一步`。
|
||||||
- `比賽結算` 需要長按 `1` 秒才會觸發,避免誤觸。
|
- `比賽結算` 需要長按 `1` 秒才會觸發,避免誤觸。
|
||||||
- 達標分數後有獲勝動畫與結算流程。
|
- 達標分數後有獲勝動畫與結算流程。
|
||||||
|
- 手機上會盡量壓縮成單頁滿版,避免上下滑動。
|
||||||
- 羽球規則
|
- 羽球規則
|
||||||
- 預設 `21` 分制,可在設定隊伍時調整目標分數。
|
- 預設 `21` 分制,可在設定隊伍時調整目標分數。
|
||||||
- 支援 Deuce:`20:20` 後需領先 `2` 分才獲勝。
|
- 支援 Deuce:`20:20` 後需領先 `2` 分才獲勝。
|
||||||
@@ -76,6 +77,13 @@ npm run lint
|
|||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 記分板滿版模式
|
||||||
|
|
||||||
|
- 記分板頁面會套用 `100dvh` 高度。
|
||||||
|
- 手機進入記分板時會關閉頁面捲動與 overscroll。
|
||||||
|
- `viewport` 已加上 `viewport-fit=cover`,較能貼合 iPhone / iPad 安全區。
|
||||||
|
- 若手機高度較矮,會再縮小字級、按鈕與分數區,盡量維持整頁顯示。
|
||||||
|
|
||||||
## 環境變數
|
## 環境變數
|
||||||
|
|
||||||
請先建立 `.env`:
|
請先建立 `.env`:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<link rel="icon" type="image/png" sizes="64x64" href="/favicon.png" />
|
<link rel="icon" type="image/png" sizes="64x64" href="/favicon.png" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||||
<link rel="manifest" href="/manifest.webmanifest" />
|
<link rel="manifest" href="/manifest.webmanifest" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||||
<meta name="theme-color" content="#143f49" />
|
<meta name="theme-color" content="#143f49" />
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
|
|||||||
168
src/App.css
168
src/App.css
@@ -9,6 +9,13 @@
|
|||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-shell-scoreboard-fit {
|
||||||
|
min-height: 100dvh;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto minmax(0, 1fr);
|
||||||
|
padding-bottom: max(12px, env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
|
||||||
.topbar {
|
.topbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -487,6 +494,8 @@
|
|||||||
grid-template-columns: minmax(0, 1fr) 160px;
|
grid-template-columns: minmax(0, 1fr) 160px;
|
||||||
gap: 14px;
|
gap: 14px;
|
||||||
align-items: start;
|
align-items: start;
|
||||||
|
min-height: 0;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.streak-banner {
|
.streak-banner {
|
||||||
@@ -625,6 +634,7 @@
|
|||||||
.scoreboard-court {
|
.scoreboard-court {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 14px;
|
gap: 14px;
|
||||||
|
min-height: 0;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-radius: 22px;
|
border-radius: 22px;
|
||||||
background:
|
background:
|
||||||
@@ -1782,6 +1792,12 @@
|
|||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-shell-scoreboard-fit {
|
||||||
|
min-height: 100dvh;
|
||||||
|
padding-top: max(8px, env(safe-area-inset-top));
|
||||||
|
padding-bottom: max(8px, env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
|
||||||
.panel {
|
.panel {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
@@ -1850,6 +1866,31 @@
|
|||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-shell-scoreboard-fit .topbar-compact {
|
||||||
|
gap: 6px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-shell-scoreboard-fit .eyebrow {
|
||||||
|
margin-bottom: 6px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 0.66rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-shell-scoreboard-fit .branding h1 {
|
||||||
|
margin: 8px 0 6px;
|
||||||
|
font-size: 1.12rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-shell-scoreboard-fit .topnav {
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-shell-scoreboard-fit .nav-pill {
|
||||||
|
padding: 7px 10px;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
.streak-banner {
|
.streak-banner {
|
||||||
top: 12%;
|
top: 12%;
|
||||||
min-width: min(92vw, 360px);
|
min-width: min(92vw, 360px);
|
||||||
@@ -1888,19 +1929,19 @@
|
|||||||
|
|
||||||
.scoreboard-team-head {
|
.scoreboard-team-head {
|
||||||
grid-template-columns: minmax(0, 1fr) 54px;
|
grid-template-columns: minmax(0, 1fr) 54px;
|
||||||
gap: 6px;
|
gap: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.team-head-main {
|
.team-head-main {
|
||||||
gap: 6px;
|
gap: 5px;
|
||||||
min-height: 52px;
|
min-height: 46px;
|
||||||
padding: 6px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scoreboard-name-chip {
|
.scoreboard-name-chip {
|
||||||
gap: 6px;
|
gap: 5px;
|
||||||
min-height: 38px;
|
min-height: 34px;
|
||||||
padding: 4px 6px;
|
padding: 3px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.serve-lane-arrow {
|
.serve-lane-arrow {
|
||||||
@@ -1908,87 +1949,87 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.team-number {
|
.team-number {
|
||||||
width: 28px;
|
width: 24px;
|
||||||
height: 28px;
|
height: 24px;
|
||||||
flex-basis: 28px;
|
flex-basis: 24px;
|
||||||
font-size: 0.9rem;
|
font-size: 0.82rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scoreboard-name-chip strong {
|
.scoreboard-name-chip strong {
|
||||||
font-size: clamp(0.82rem, 4vw, 1.1rem);
|
font-size: clamp(0.76rem, 3.6vw, 1rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.team-icon-button {
|
.team-icon-button {
|
||||||
min-height: 32px;
|
min-height: 30px;
|
||||||
font-size: 1rem;
|
font-size: 0.94rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.serve-lane {
|
.serve-lane {
|
||||||
grid-template-columns: 28px auto;
|
grid-template-columns: 28px auto;
|
||||||
min-height: 38px;
|
min-height: 34px;
|
||||||
padding: 6px 8px;
|
padding: 4px 6px;
|
||||||
font-size: 0.92rem;
|
font-size: 0.86rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.serve-lane small {
|
.serve-lane small {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
justify-self: start;
|
justify-self: start;
|
||||||
font-size: 0.78rem;
|
font-size: 0.72rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.score-panel-surface {
|
.score-panel-surface {
|
||||||
min-height: 112px;
|
min-height: clamp(90px, 15.5dvh, 104px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.score-panel-value {
|
.score-panel-value {
|
||||||
font-size: clamp(3.5rem, 18vw, 5.4rem);
|
font-size: clamp(3rem, 15vw, 4.6rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scoreboard-center-banner {
|
.scoreboard-center-banner {
|
||||||
gap: 4px;
|
gap: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scoreboard-center-banner p {
|
.scoreboard-center-banner p {
|
||||||
font-size: 1.25rem;
|
font-size: 1.02rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scoreboard-center-banner small {
|
.scoreboard-center-banner small {
|
||||||
font-size: 0.78rem;
|
font-size: 0.7rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scoreboard-rail {
|
.scoreboard-rail {
|
||||||
grid-template-columns: minmax(0, 1fr) 108px 118px;
|
grid-template-columns: minmax(0, 1fr) 94px 106px;
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rail-square-button {
|
|
||||||
min-height: 42px;
|
|
||||||
font-size: 0.95rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rail-clock {
|
|
||||||
min-height: 42px;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rail-room-id {
|
|
||||||
padding: 8px 10px;
|
|
||||||
font-size: 0.86rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rail-pill {
|
|
||||||
padding: 10px 8px;
|
|
||||||
font-size: 0.92rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rail-pill-hold-wrap {
|
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rail-square-button {
|
||||||
|
min-height: 38px;
|
||||||
|
font-size: 0.84rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rail-clock {
|
||||||
|
min-height: 38px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 0.96rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rail-room-id {
|
||||||
|
padding: 7px 8px;
|
||||||
|
font-size: 0.76rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rail-pill {
|
||||||
|
padding: 8px 7px;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rail-pill-hold-wrap {
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.rail-hold-progress {
|
.rail-hold-progress {
|
||||||
height: 6px;
|
height: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.finish-dialog {
|
.finish-dialog {
|
||||||
@@ -2171,6 +2212,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 720px) and (max-height: 840px) {
|
||||||
|
.app-shell-scoreboard-fit .scoreboard-screen {
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-shell-scoreboard-fit .scoreboard-court {
|
||||||
|
gap: 8px;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-shell-scoreboard-fit .scoreboard-team-section {
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-shell-scoreboard-fit .score-panel-surface {
|
||||||
|
min-height: clamp(82px, 14.2dvh, 96px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-shell-scoreboard-fit .score-panel-value {
|
||||||
|
font-size: clamp(2.7rem, 13.5vw, 4.1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-shell-scoreboard-fit .scoreboard-center-banner p {
|
||||||
|
font-size: 0.96rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.room-list-grid {
|
.room-list-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
|
|||||||
10
src/App.tsx
10
src/App.tsx
@@ -219,6 +219,14 @@ function App() {
|
|||||||
return () => window.clearTimeout(timer)
|
return () => window.clearTimeout(timer)
|
||||||
}, [navigationLockMessage])
|
}, [navigationLockMessage])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.body.classList.toggle('body-scoreboard', isScoreboardRoute)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.body.classList.remove('body-scoreboard')
|
||||||
|
}
|
||||||
|
}, [isScoreboardRoute])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handlePwaUpdateReady = () => {
|
const handlePwaUpdateReady = () => {
|
||||||
setPwaUpdateReady(true)
|
setPwaUpdateReady(true)
|
||||||
@@ -908,7 +916,7 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={isScoreboardRoute ? 'app-shell app-shell-scoreboard' : 'app-shell'}>
|
<div className={isScoreboardRoute ? 'app-shell app-shell-scoreboard app-shell-scoreboard-fit' : 'app-shell'}>
|
||||||
<header className={isScoreboardRoute ? 'topbar topbar-compact' : 'topbar'}>
|
<header className={isScoreboardRoute ? 'topbar topbar-compact' : 'topbar'}>
|
||||||
<div className="branding">
|
<div className="branding">
|
||||||
<p className="eyebrow">Badminton Scoreboard</p>
|
<p className="eyebrow">Badminton Scoreboard</p>
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ body {
|
|||||||
linear-gradient(180deg, var(--page-bg), var(--page-bg-2));
|
linear-gradient(180deg, var(--page-bg), var(--page-bg-2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.body-scoreboard {
|
||||||
|
overflow: hidden;
|
||||||
|
overscroll-behavior: none;
|
||||||
|
}
|
||||||
|
|
||||||
body::before {
|
body::before {
|
||||||
content: '';
|
content: '';
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -53,6 +58,11 @@ body::before {
|
|||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.body-scoreboard #root {
|
||||||
|
min-height: 100dvh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
input,
|
input,
|
||||||
textarea,
|
textarea,
|
||||||
select,
|
select,
|
||||||
|
|||||||
Reference in New Issue
Block a user