Files
vigilcare-clinical/vigilcare-dashboard/src/composables/useReplayControls.js
T
2026-06-20 14:41:11 +08:00

138 lines
3.3 KiB
JavaScript

import { ref, computed } from 'vue'
const TICK_MS = 100
export function useReplayControls() {
const isPaused = ref(true)
const speed = ref(60)
const scenarioStartMs = ref(0)
const scenarioEndMs = ref(0)
const currentMs = ref(0)
let timer = null
const scenarioDurationMinutes = computed(() => {
const span = scenarioEndMs.value - scenarioStartMs.value
return span > 0 ? span / 60_000 : 0
})
const currentOffsetMinutes = computed(() => {
const span = currentMs.value - scenarioStartMs.value
return span > 0 ? span / 60_000 : 0
})
const progress = computed(() => {
const span = scenarioEndMs.value - scenarioStartMs.value
if (span <= 0) return 0
return Math.min(100, ((currentMs.value - scenarioStartMs.value) / span) * 100)
})
const formattedTime = computed(() => {
const mins = currentOffsetMinutes.value
const h = Math.floor(mins / 60)
const m = Math.floor(mins % 60)
return `${h}:${String(m).padStart(2, '0')}`
})
function setScenarioBounds(startMs, endMs) {
if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || endMs <= startMs) return
scenarioStartMs.value = startMs
scenarioEndMs.value = endMs
if (currentMs.value < startMs || currentMs.value > endMs) {
currentMs.value = endMs
}
}
function syncToEnd() {
if (scenarioEndMs.value > scenarioStartMs.value) {
currentMs.value = scenarioEndMs.value
}
}
function pause() {
isPaused.value = true
stopTick()
}
function resume() {
if (scenarioEndMs.value <= scenarioStartMs.value) return
if (currentMs.value >= scenarioEndMs.value) {
currentMs.value = scenarioStartMs.value
}
isPaused.value = false
startTick()
}
function setSpeed(s) {
speed.value = s
if (s === 0) {
currentMs.value = scenarioEndMs.value
pause()
}
}
function jumpToOffset(minutes) {
if (scenarioEndMs.value <= scenarioStartMs.value) return
const target = scenarioStartMs.value + minutes * 60_000
currentMs.value = Math.min(Math.max(target, scenarioStartMs.value), scenarioEndMs.value)
}
function jumpToTimestamp(iso) {
if (!iso || scenarioEndMs.value <= scenarioStartMs.value) return
const ms = new Date(iso).getTime()
currentMs.value = Math.min(Math.max(ms, scenarioStartMs.value), scenarioEndMs.value)
}
function isAtOrBefore(iso) {
if (!iso) return true
return new Date(iso).getTime() <= currentMs.value
}
function tick() {
if (isPaused.value || scenarioEndMs.value <= scenarioStartMs.value) return
if (speed.value === 0) {
currentMs.value = scenarioEndMs.value
pause()
return
}
currentMs.value = Math.min(currentMs.value + TICK_MS * speed.value, scenarioEndMs.value)
if (currentMs.value >= scenarioEndMs.value) pause()
}
function startTick() {
stopTick()
timer = setInterval(tick, TICK_MS)
}
function stopTick() {
if (timer) clearInterval(timer)
timer = null
}
function stopPlayback() {
stopTick()
}
return {
isPaused,
speed,
scenarioStartMs,
scenarioEndMs,
currentMs,
scenarioDurationMinutes,
currentOffsetMinutes,
progress,
formattedTime,
setScenarioBounds,
syncToEnd,
pause,
resume,
setSpeed,
jumpToOffset,
jumpToTimestamp,
isAtOrBefore,
stopPlayback,
}
}