調整比賽結算長按回饋並更新 README
This commit is contained in:
@@ -98,6 +98,7 @@ export function ScoreboardPage({
|
||||
onSwapTeamPlayers,
|
||||
onUndoLastPoint,
|
||||
}: ScoreboardPageProps) {
|
||||
const FINISH_HOLD_DURATION = 1500
|
||||
const [pickerOpen, setPickerOpen] = useState(false)
|
||||
const [settingsOpen, setSettingsOpen] = useState(false)
|
||||
const [draftPlayers, setDraftPlayers] = useState<string[]>([])
|
||||
@@ -105,9 +106,14 @@ export function ScoreboardPage({
|
||||
String(scoreState.targetScore),
|
||||
)
|
||||
const [clock, setClock] = useState(() => formatClock())
|
||||
const [finishHoldActive, setFinishHoldActive] = useState(false)
|
||||
const [finishHoldProgress, setFinishHoldProgress] = useState(0)
|
||||
const [voiceSettings, setVoiceSettings] = useState<VoiceSettings>(() =>
|
||||
loadVoiceSettings(),
|
||||
)
|
||||
const finishHoldFrameRef = useRef<number | null>(null)
|
||||
const finishHoldStartRef = useRef(0)
|
||||
const finishTriggeredRef = useRef(false)
|
||||
const lastAnnouncedPointRef = useRef(0)
|
||||
const previousScoresRef = useRef({ left: 0, right: 0 })
|
||||
|
||||
@@ -128,6 +134,10 @@ export function ScoreboardPage({
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (finishHoldFrameRef.current !== null) {
|
||||
window.cancelAnimationFrame(finishHoldFrameRef.current)
|
||||
}
|
||||
|
||||
if ('speechSynthesis' in window) {
|
||||
window.speechSynthesis.cancel()
|
||||
}
|
||||
@@ -301,6 +311,57 @@ export function ScoreboardPage({
|
||||
setPickerOpen(true)
|
||||
}
|
||||
|
||||
const stopFinishHold = () => {
|
||||
if (finishHoldFrameRef.current !== null) {
|
||||
window.cancelAnimationFrame(finishHoldFrameRef.current)
|
||||
finishHoldFrameRef.current = null
|
||||
}
|
||||
|
||||
finishHoldStartRef.current = 0
|
||||
finishTriggeredRef.current = false
|
||||
setFinishHoldActive(false)
|
||||
setFinishHoldProgress(0)
|
||||
}
|
||||
|
||||
const startFinishHold = () => {
|
||||
if (finishDialogOpen || finishDialogUploading || finishHoldActive) {
|
||||
return
|
||||
}
|
||||
|
||||
finishTriggeredRef.current = false
|
||||
finishHoldStartRef.current = performance.now()
|
||||
setFinishHoldActive(true)
|
||||
setFinishHoldProgress(0)
|
||||
|
||||
const tick = (now: number) => {
|
||||
const elapsed = now - finishHoldStartRef.current
|
||||
const progress = Math.min(elapsed / FINISH_HOLD_DURATION, 1)
|
||||
setFinishHoldProgress(progress)
|
||||
|
||||
if (progress >= 1) {
|
||||
finishTriggeredRef.current = true
|
||||
setFinishHoldActive(false)
|
||||
setFinishHoldProgress(0)
|
||||
finishHoldFrameRef.current = null
|
||||
onOpenFinishDialog()
|
||||
return
|
||||
}
|
||||
|
||||
finishHoldFrameRef.current = window.requestAnimationFrame(tick)
|
||||
}
|
||||
|
||||
finishHoldFrameRef.current = window.requestAnimationFrame(tick)
|
||||
}
|
||||
|
||||
const cancelFinishHold = () => {
|
||||
if (finishTriggeredRef.current) {
|
||||
finishTriggeredRef.current = false
|
||||
return
|
||||
}
|
||||
|
||||
stopFinishHold()
|
||||
}
|
||||
|
||||
const toggleDraftPlayer = (playerName: string) => {
|
||||
setDraftPlayers((current) => {
|
||||
if (current.includes(playerName)) {
|
||||
@@ -461,9 +522,48 @@ export function ScoreboardPage({
|
||||
|
||||
<div className="rail-clock">{clock}</div>
|
||||
|
||||
<button className="rail-pill rail-pill-danger" type="button" onClick={onOpenFinishDialog}>
|
||||
比賽結算
|
||||
</button>
|
||||
<div
|
||||
className={
|
||||
finishHoldActive ? 'rail-pill-hold-wrap rail-pill-hold-wrap-active' : 'rail-pill-hold-wrap'
|
||||
}
|
||||
>
|
||||
<button
|
||||
className={
|
||||
finishHoldActive
|
||||
? 'rail-pill rail-pill-danger rail-pill-active-hold'
|
||||
: 'rail-pill rail-pill-danger'
|
||||
}
|
||||
type="button"
|
||||
onPointerDown={startFinishHold}
|
||||
onPointerUp={cancelFinishHold}
|
||||
onPointerLeave={cancelFinishHold}
|
||||
onPointerCancel={cancelFinishHold}
|
||||
onBlur={cancelFinishHold}
|
||||
onKeyDown={(event) => {
|
||||
if ((event.key === 'Enter' || event.key === ' ') && !event.repeat) {
|
||||
event.preventDefault()
|
||||
startFinishHold()
|
||||
}
|
||||
}}
|
||||
onKeyUp={(event) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault()
|
||||
cancelFinishHold()
|
||||
}
|
||||
}}
|
||||
>
|
||||
比賽結算
|
||||
</button>
|
||||
|
||||
{finishHoldActive ? (
|
||||
<div aria-hidden="true" className="rail-hold-progress">
|
||||
<span
|
||||
className="rail-hold-progress-bar"
|
||||
style={{ transform: `scaleX(${finishHoldProgress})` }}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user