Skip to content

Commit

Permalink
feat: 添加点名时的背景设置
Browse files Browse the repository at this point in the history
  • Loading branch information
typed-sigterm committed Jun 25, 2024
1 parent 6cbb3ee commit ac634ba
Show file tree
Hide file tree
Showing 16 changed files with 328 additions and 169 deletions.
2 changes: 1 addition & 1 deletion app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { promiseTimeout } from '@vueuse/core'
import { zhCN } from 'naive-ui'
if (IN_APP)
setupTheme() // 异步初始化主题
await useThemeStore().init() // 异步初始化主题
const loading = ref(true)
const show = ref(false)
Expand Down
68 changes: 68 additions & 0 deletions components/background.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<script lang="ts" setup>
export type Status = 'normal' | 'ready-rolling' | 'rolling' | 'pausing'
/** 当前状态(决定显示的背景) */
const status = defineModel<Status>('status')
const theme = useThemeStore()
// 预加载背景视频
watchImmediate(() => theme.backgroundRolling?.url, (url) => {
if (!url)
return
useHead({
link: [{
rel: 'prefetch',
href: url,
}],
})
})
const videoElement = ref<HTMLVideoElement | null>(null)
const rollingUsingVideo = computed(() => { // backgroundRolling 使用视频
return theme.backgroundRolling && theme.backgroundRolling.url
})
whenever(() => status.value === 'pausing', () => {
videoElement.value?.pause()
})
const [DefineDefaultBackground, DefaultBackground] = createReusableTemplate()
</script>

<template>
<DefineDefaultBackground>
<img v-if="theme.background" class="bg" :src="theme.background.url">
</DefineDefaultBackground>

<DefaultBackground v-if="status === 'normal'" />
<template v-else>
<!-- 使用 v-show 避免多次加载视频资源 -->
<video
v-show="rollingUsingVideo"
ref="videoElement"
class="bg"
autoplay
@play="status = 'rolling'"
>
<source
:src="theme.backgroundRolling?.url"
:type="theme.properties.backgroundRolling.mimeType"
>
<DefaultBackground />
</video>
<DefaultBackground v-show="!rollingUsingVideo" disablepictureinpicture />
</template>
</template>
<style scoped>
.bg {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
object-fit: cover;
z-index: -1;
}
</style>
48 changes: 29 additions & 19 deletions components/ex-caller.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<script lang="ts" setup>
import type { Status as ResultStatus } from './result-board.vue'
import type { Status as BackgroundStatus } from './background.vue'
import IconSettings from '~icons/ep/setting'
setupUiHooks()
const config = useConfigStore()
const theme = useThemeStore()
function getRollCall(options?: Partial<RollCallConfig>) {
return useRollCall({ // 点名结果
Expand All @@ -14,25 +15,36 @@ function getRollCall(options?: Partial<RollCallConfig>) {
}
const result = getRollCall()
const unstarted = ref(true) // 是否未开始过
const showingResume = ref(false) // 是否正在播放动画
const status = ref<ResultStatus>('paused')
const backgroundStatus = ref<BackgroundStatus>('normal')
function handleStart() {
result.value.start()
unstarted.value = false
triggerStopCallingGuide()
backgroundStatus.value = 'ready-rolling'
const stopWatch = whenever( // 背景准备好后开始抽取
() => backgroundStatus.value === 'rolling',
() => {
stopWatch()
status.value = 'rolling'
result.value.start()
triggerStopCallingGuide()
},
)
}
function handlePause() {
if (!result.value?.isActive || showingResume.value)
return
showingResume.value = true
function handlePausing() {
result.value.pause()
status.value = 'pausing'
backgroundStatus.value = 'pausing'
if (config.plan.enabled && config.plan.queue.length > 0) { // 有计划则执行计划
result.value.currentValue = config.plan.queue[0]
config.plan.queue.shift()
}
}
function handlePaused() {
status.value = 'paused'
backgroundStatus.value = 'normal'
}
const loadSettings = ref(false) // 是否需要加载设置组件
const loadedSettings = ref(false) // 设置组件是否已经加载完成
Expand All @@ -59,22 +71,20 @@ prefetchComponents('LazySettings')
</script>

<template>
<Background v-model:status="backgroundStatus" />

<ResultBoard
v-bind="$attrs"
v-model:showing-resume="showingResume"
v-model:status="status"
class="bg-no-repeat bg-center bg-cover"
:style="{
userSelect: result.isActive ? 'none' : 'auto',
backgroundImage: theme.background && `url(${theme.background.url})`,
}"
:value="result.currentValue"
:show-resume="!result.isActive"
:confetti="config.ui.confetti"
data-guide-id="result-board"
@start="handleStart"
@pause="handlePause"
@pausing="handlePausing"
@paused="handlePaused"
>
<template v-if="config.ui.settingsButton === 'center'" #startOperators>
<template v-if="config.ui.settingsButton === 'center'" #startExtraOperators>
<LargeButton
:type="config.plan.enabled ? 'error' : 'info'"
:loading="showLoadingSettings"
Expand All @@ -89,7 +99,7 @@ prefetchComponents('LazySettings')
</LargeButton>
</template>

<template v-if="config.ui.settingsButton === 'center'" #resumeOperators>
<template v-if="config.ui.settingsButton === 'center'" #resumeExtraOperators>
<NButton circle @click="handleOpenSettings">
<template #icon>
<NIcon :size="18">
Expand Down
54 changes: 24 additions & 30 deletions components/result-board.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@ import { promiseTimeout } from '@vueuse/core'
import IconStart from '~icons/ant-design/caret-right-filled'
import IconResume from '~icons/ant-design/play-circle-filled'
export type Status = 'rolling' | 'pausing' | 'paused'
const props = withDefaults(defineProps<{
/** 当前抽取到的值 */
value?: string
/**
* 是否显示“继续抽取”按钮
* @default true
*/
showResume?: boolean
/**
* 显示“继续抽取”按钮后,是否显示彩带效果
* @default true
Expand All @@ -21,83 +18,80 @@ const props = withDefaults(defineProps<{
confetti: true,
})
const emit = defineEmits<{
/** 开始抽取 */
(ev: 'start'): void
/** 暂停抽取 */
(ev: 'pause'): void
(ev: 'pausing'): void
(ev: 'paused'): void
}>()
defineSlots<{
startOperators: () => any
resumeOperators: () => any
startExtraOperators: () => any
resumeExtraOperators: () => any
}>()
const showingResume = defineModel<boolean>('showingResume', { required: true })
const status = defineModel<Status>('status')
const beforeAnimation = ref(false)
watch(showingResume, async (v) => {
if (!v)
return
whenever(() => status.value === 'pausing', async (pausing) => {
// 停止后等待 1s
beforeAnimation.value = true
await promiseTimeout(1000)
beforeAnimation.value = false
if (!pausing)
return
// 播放特效
if (props.confetti)
confetti.addConfetti()
// 再等待 1s 显示按钮
await promiseTimeout(1000)
if (!v)
if (!pausing)
return
showingResume.value = false
emit('paused')
})
function handlePause() {
if (props.showResume) // 已经暂停
if (status.value !== 'rolling')
return
emit('pause')
emit('pausing')
}
</script>

<template>
<NSpace
class="h-full items-center"
:class="[showingResume && 'showing-resume']"
:class="[
status === 'pausing' && 'showing-resume',
status === 'paused' ? 'select-auto' : 'select-none',
]"
vertical
justify="center"
@click="handlePause"
>
<NSpace v-if="value === undefined">
<LargeButton type="primary" data-guide-id="start-button" @click.stop="emit('start')">
<LargeButton type="primary" data-guide-id="start-button" @click.stop="$emit('start')">
<template #icon>
<NIcon class="ml-1" :size="36">
<IconStart />
</NIcon>
</template>
</LargeButton>
<slot name="startOperators" />
<slot name="startExtraOperators" />
</NSpace>

<template v-else>
<p class="mb-2 text-5xl">
{{ value }}
</p>
<NSpace v-if="showResume && !beforeAnimation" class="resume-operators">
<NButton class="resume-button" @click.stop="emit('start')">
<NSpace v-if="status === 'paused'" class="resume-operators">
<NButton class="resume-button" @click.stop="$emit('start')">
继续点名
<template #icon>
<NIcon :size="20">
<IconResume />
</NIcon>
</template>
</NButton>
<slot name="resumeOperators" />
<slot name="resumeExtraOperators" />
</NSpace>
</template>
</NSpace>
</template>

<style scoped>
<style scoped>
.showing-resume .resume-operators {
animation: show-resume-operators .65s forwards;
}
Expand Down
4 changes: 1 addition & 3 deletions components/settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ async function handleShowOrClosePlan(show: boolean) {
</template>
</SettingsSubEntry>
<SettingsSubEntry title="主题设置" only-in-app>
<template #default="{ close }">
<SettingsTheme @commit="close" @discard="close" />
</template>
<SettingsTheme />
<template #icon>
<IconPictureFilled />
</template>
Expand Down
Loading

0 comments on commit ac634ba

Please sign in to comment.