102 lines
2.5 KiB
Vue
Raw Normal View History

2026-05-31 13:31:05 +02:00
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import { useNav } from '@slidev/client'
const { isPresenter, currentSlideNo, nextSlide } = useNav()
const LIMIT = 20
const elapsed = ref(0)
const hasAdvanced = ref(false)
const paused = ref(true)
let timer: ReturnType<typeof setInterval> | null = null
const togglePause = () => {
paused.value = !paused.value
}
const advanceSlide = () => {
if (hasAdvanced.value || elapsed.value < LIMIT)
return
hasAdvanced.value = true
nextSlide()
}
onMounted(() => {
timer = setInterval(() => {
if (paused.value)
return
elapsed.value++
advanceSlide()
}, 1000)
})
onUnmounted(() => {
if (timer)
clearInterval(timer)
timer = null
})
watch(currentSlideNo, () => {
elapsed.value = 0
hasAdvanced.value = false
paused.value = false
})
const remaining = computed(() => LIMIT - elapsed.value)
const over = computed(() => remaining.value < 0)
const warn = computed(() => !over.value && remaining.value <= 5)
const pct = computed(() => Math.min(100, (elapsed.value / LIMIT) * 100))
const label = computed(() => `${over.value ? '+' : ''}${Math.abs(remaining.value)}s`)
</script>
<template>
<div v-if="isPresenter" class="pk-timer" :class="{ over, warn, paused }" @click="togglePause">
<div class="pk-bar" :style="{ width: pct + '%' }" />
<span class="pk-label">{{ paused ? '⏸' : label }}</span>
</div>
</template>
<style scoped>
.pk-timer {
position: fixed;
top: 12px;
right: 12px;
z-index: 9999;
width: 130px;
height: 38px;
border-radius: 10px;
background: rgba(20, 20, 25, 0.85);
border: 1px solid rgba(255, 255, 255, 0.15);
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
font-family: 'Fira Code', monospace;
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
cursor: pointer;
user-select: none;
}
.pk-timer.paused { border-color: rgba(96, 165, 250, 0.8); }
.pk-timer.paused .pk-bar { background: rgba(96, 165, 250, 0.35); }
.pk-bar {
position: absolute;
left: 0;
top: 0;
bottom: 0;
background: rgba(74, 222, 128, 0.35);
transition: width 1s linear;
}
.pk-timer.warn .pk-bar { background: rgba(251, 191, 36, 0.4); }
.pk-timer.over { border-color: rgba(248, 113, 113, 0.8); }
.pk-timer.over .pk-bar { width: 100% !important; background: rgba(248, 113, 113, 0.45); }
.pk-label {
position: relative;
font-size: 20px;
font-weight: 700;
color: #fff;
letter-spacing: 0.5px;
}
.pk-timer.over .pk-label { color: #fecaca; }
</style>