feat: 添加计时工具(#156)

This commit is contained in:
pipipi-pikachu 2022-10-05 18:47:29 +08:00
parent 09acb9a2d5
commit 42541f678d
5 changed files with 227 additions and 0 deletions

View File

@ -61,6 +61,7 @@ npm run serve
- 幻灯片模板 - 幻灯片模板
- 翻页动画 - 翻页动画
- 元素动画(入场、退场、强调) - 元素动画(入场、退场、强调)
- 选择面板(隐藏元素、层级排序、元素命名)
### 幻灯片元素编辑 ### 幻灯片元素编辑
- 元素添加、删除 - 元素添加、删除
- 元素复制粘贴 - 元素复制粘贴
@ -140,6 +141,7 @@ npm run serve
### 幻灯片放映 ### 幻灯片放映
- 全部幻灯片预览 - 全部幻灯片预览
- 画笔、黑板工具 - 画笔、黑板工具
- 计时器工具
- 激光笔 - 激光笔
- 自动放映 - 自动放映
- 演讲者视图 - 演讲者视图

View File

@ -111,6 +111,7 @@ import {
FormatBrush, FormatBrush,
PreviewOpen, PreviewOpen,
PreviewClose, PreviewClose,
StopwatchStart,
} from '@icon-park/vue-next' } from '@icon-park/vue-next'
export const icons = { export const icons = {
@ -223,6 +224,7 @@ export const icons = {
IconFormatBrush: FormatBrush, IconFormatBrush: FormatBrush,
IconPreviewOpen: PreviewOpen, IconPreviewOpen: PreviewOpen,
IconPreviewClose: PreviewClose, IconPreviewClose: PreviewClose,
IconStopwatchStart: StopwatchStart,
} }
export default { export default {

View File

@ -25,6 +25,11 @@
@close="writingBoardToolVisible = false" @close="writingBoardToolVisible = false"
/> />
<CountdownTimer
v-if="timerlVisible"
@close="timerlVisible = false"
/>
<div class="tools-left"> <div class="tools-left">
<IconLeftTwo class="tool-btn" theme="two-tone" :fill="['#111', '#fff']" @click="execPrev()" /> <IconLeftTwo class="tool-btn" theme="two-tone" :fill="['#111', '#fff']" @click="execPrev()" />
<IconRightTwo class="tool-btn" theme="two-tone" :fill="['#111', '#fff']" @click="execNext()" /> <IconRightTwo class="tool-btn" theme="two-tone" :fill="['#111', '#fff']" @click="execNext()" />
@ -43,6 +48,9 @@
<Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.3" title="激光笔"> <Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.3" title="激光笔">
<IconMagic class="tool-btn" :class="{ 'active': laserPen }" @click="laserPen = !laserPen" /> <IconMagic class="tool-btn" :class="{ 'active': laserPen }" @click="laserPen = !laserPen" />
</Tooltip> </Tooltip>
<Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.3" title="计时器">
<IconStopwatchStart class="tool-btn" :class="{ 'active': timerlVisible }" @click="timerlVisible = !timerlVisible" />
</Tooltip>
<Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.3" title="演讲者视图"> <Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.3" title="演讲者视图">
<IconListView class="tool-btn" @click="changeViewMode('presenter')" /> <IconListView class="tool-btn" @click="changeViewMode('presenter')" />
</Tooltip> </Tooltip>
@ -72,6 +80,7 @@ import useFullscreen from './hooks/useFullscreen'
import ScreenSlideList from './ScreenSlideList.vue' import ScreenSlideList from './ScreenSlideList.vue'
import SlideThumbnails from './SlideThumbnails.vue' import SlideThumbnails from './SlideThumbnails.vue'
import WritingBoardTool from './WritingBoardTool.vue' import WritingBoardTool from './WritingBoardTool.vue'
import CountdownTimer from './CountdownTimer.vue'
const props = defineProps({ const props = defineProps({
changeViewMode: { changeViewMode: {
@ -104,6 +113,7 @@ const { fullscreenState, manualExitFullscreen } = useFullscreen()
const rightToolsVisible = ref(false) const rightToolsVisible = ref(false)
const writingBoardToolVisible = ref(false) const writingBoardToolVisible = ref(false)
const timerlVisible = ref(false)
const slideThumbnailModelVisible = ref(false) const slideThumbnailModelVisible = ref(false)
const laserPen = ref(false) const laserPen = ref(false)

View File

@ -0,0 +1,204 @@
<template>
<MoveablePanel
class="countdown-timer"
:width="180"
:height="110"
:left="left"
:top="top"
>
<div class="header">
<span class="text-btn" @click="toggle()">{{ inTiming ? '暂停' : '开始'}}</span>
<span class="text-btn" @click="reset()">重置</span>
<span class="text-btn" @click="toggleCountdown()" :class="{ 'active': isCountdown }">倒计时</span>
</div>
<div class="content">
<div class="timer">
<input
type="text"
:value="fillDigit(minute, 2)"
:maxlength="3" :disabled="inputEditable"
@mousedown.stop
@blur="$event => changeTime($event, 'minute')"
@keydown.stop
@keydown.enter.stop="$event => changeTime($event, 'minute')"
>
</div>
<div class="colon">:</div>
<div class="timer">
<input
type="text"
:value="fillDigit(second, 2)"
:maxlength="3" :disabled="inputEditable"
@mousedown.stop
@blur="$event => changeTime($event, 'second')"
@keydown.stop
@keydown.enter.stop="$event => changeTime($event, 'second')"
>
</div>
</div>
<div class="close-btn" @click="emit('close')"><IconClose class="icon" /></div>
</MoveablePanel>
</template>
<script lang="ts" setup>
import { computed, onUnmounted, ref } from 'vue'
import { fillDigit } from '@/utils/common'
defineProps({
left: {
type: Number,
default: 5,
},
top: {
type: Number,
default: 5,
},
})
const emit = defineEmits<{
(event: 'close'): void
}>()
const timer = ref<number | null>(null)
const inTiming = ref(false)
const isCountdown = ref(false)
const time = ref(0)
const minute = computed(() => Math.floor(time.value / 60))
const second = computed(() => time.value % 60)
const inputEditable = computed(() => {
return !isCountdown.value || inTiming.value
})
const clearTimer = () => {
if (timer.value) clearInterval(timer.value)
}
onUnmounted(clearTimer)
const pause = () => {
clearTimer()
inTiming.value = false
}
const reset = () => {
clearTimer()
inTiming.value = false
if (isCountdown.value) time.value = 600
else time.value = 0
}
const start = () => {
clearTimer()
if (isCountdown.value) {
timer.value = setInterval(() => {
time.value = time.value - 1
if (time.value <= 0) reset()
}, 1000)
}
else {
timer.value = setInterval(() => {
time.value = time.value + 1
if (time.value > 36000) pause()
}, 1000)
}
inTiming.value = true
}
const toggle = () => {
if (inTiming.value) pause()
else start()
}
const toggleCountdown = () => {
isCountdown.value = !isCountdown.value
reset()
}
const changeTime = (e: FocusEvent | KeyboardEvent, type: 'minute' | 'second') => {
const inputRef = e.target as HTMLInputElement
let value = inputRef.value
const isNumber = /^(\d)+$/.test(value)
if (isNumber) {
if (type === 'second' && +value >= 60) value = '59'
time.value = type === 'minute' ? (+value * 60 + second.value) : (+value + minute.value * 60)
}
else inputRef.value = type === 'minute' ? fillDigit(minute.value, 2) : fillDigit(second.value, 2)
}
</script>
<style lang="scss" scoped>
.countdown-timer {
user-select: none;
}
.header {
height: 16px;
font-size: 13px;
margin-bottom: 16px;
display: flex;
align-items: center;
.text-btn {
margin-right: 8px;
cursor: pointer;
&:hover, &.active {
color: $themeColor;
}
}
}
.content {
display: flex;
justify-content: space-between;
padding: 0 5px;
}
.timer {
width: 54px;
height: 54px;
border-radius: 50%;
background-color: rgba($color: $themeColor, $alpha: .05);
font-size: 22px;
overflow: hidden;
input {
width: 100%;
height: 100%;
border: 0;
outline: 0;
background-color: transparent;
text-align: center;
}
}
.colon {
height: 54px;
line-height: 54px;
font-size: 22px;
}
.icon-btn {
width: 20px;
height: 20px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.pause, .play {
font-size: 17px;
}
.reset {
font-size: 12px;
}
.close-btn {
position: absolute;
top: 0;
right: 0;
padding: 10px;
cursor: pointer;
}
</style>

View File

@ -4,6 +4,7 @@
<div class="tool-btn" @click="changeViewMode('base')"><IconListView class="tool-icon" /><span>普通视图</span></div> <div class="tool-btn" @click="changeViewMode('base')"><IconListView class="tool-icon" /><span>普通视图</span></div>
<div class="tool-btn" :class="{ 'active': writingBoardToolVisible }" @click="writingBoardToolVisible = !writingBoardToolVisible"><IconWrite class="tool-icon" /><span>画笔</span></div> <div class="tool-btn" :class="{ 'active': writingBoardToolVisible }" @click="writingBoardToolVisible = !writingBoardToolVisible"><IconWrite class="tool-icon" /><span>画笔</span></div>
<div class="tool-btn" :class="{ 'active': laserPen }" @click="laserPen = !laserPen"><IconMagic class="tool-icon" /><span>激光笔</span></div> <div class="tool-btn" :class="{ 'active': laserPen }" @click="laserPen = !laserPen"><IconMagic class="tool-icon" /><span>激光笔</span></div>
<div class="tool-btn" :class="{ 'active': timerlVisible }" @click="timerlVisible = !timerlVisible"><IconStopwatchStart class="tool-icon" /><span>计时器</span></div>
<div class="tool-btn" @click="() => fullscreenState ? manualExitFullscreen() : enterFullscreen()"> <div class="tool-btn" @click="() => fullscreenState ? manualExitFullscreen() : enterFullscreen()">
<IconOffScreenOne class="tool-icon" v-if="fullscreenState" /> <IconOffScreenOne class="tool-icon" v-if="fullscreenState" />
<IconOffScreenOne class="tool-icon" v-else /> <IconOffScreenOne class="tool-icon" v-else />
@ -38,6 +39,12 @@
v-if="writingBoardToolVisible" v-if="writingBoardToolVisible"
@close="writingBoardToolVisible = false" @close="writingBoardToolVisible = false"
/> />
<CountdownTimer
v-if="timerlVisible"
:left="75"
@close="timerlVisible = false"
/>
</div> </div>
<div class="thumbnails" <div class="thumbnails"
ref="thumbnailsRef" ref="thumbnailsRef"
@ -85,6 +92,7 @@ import useFullscreen from './hooks/useFullscreen'
import ThumbnailSlide from '@/views/components/ThumbnailSlide/index.vue' import ThumbnailSlide from '@/views/components/ThumbnailSlide/index.vue'
import ScreenSlideList from './ScreenSlideList.vue' import ScreenSlideList from './ScreenSlideList.vue'
import WritingBoardTool from './WritingBoardTool.vue' import WritingBoardTool from './WritingBoardTool.vue'
import CountdownTimer from './CountdownTimer.vue'
const props = defineProps({ const props = defineProps({
changeViewMode: { changeViewMode: {
@ -98,6 +106,7 @@ const { slides, slideIndex, viewportRatio, currentSlide } = storeToRefs(useSlide
const slideListWrapRef = ref<HTMLElement>() const slideListWrapRef = ref<HTMLElement>()
const thumbnailsRef = ref<HTMLElement>() const thumbnailsRef = ref<HTMLElement>()
const writingBoardToolVisible = ref(false) const writingBoardToolVisible = ref(false)
const timerlVisible = ref(false)
const laserPen = ref(false) const laserPen = ref(false)
const { const {