mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
feat: 添加计时工具(#156)
This commit is contained in:
parent
09acb9a2d5
commit
42541f678d
@ -61,6 +61,7 @@ npm run serve
|
||||
- 幻灯片模板
|
||||
- 翻页动画
|
||||
- 元素动画(入场、退场、强调)
|
||||
- 选择面板(隐藏元素、层级排序、元素命名)
|
||||
### 幻灯片元素编辑
|
||||
- 元素添加、删除
|
||||
- 元素复制粘贴
|
||||
@ -140,6 +141,7 @@ npm run serve
|
||||
### 幻灯片放映
|
||||
- 全部幻灯片预览
|
||||
- 画笔、黑板工具
|
||||
- 计时器工具
|
||||
- 激光笔
|
||||
- 自动放映
|
||||
- 演讲者视图
|
||||
|
@ -111,6 +111,7 @@ import {
|
||||
FormatBrush,
|
||||
PreviewOpen,
|
||||
PreviewClose,
|
||||
StopwatchStart,
|
||||
} from '@icon-park/vue-next'
|
||||
|
||||
export const icons = {
|
||||
@ -223,6 +224,7 @@ export const icons = {
|
||||
IconFormatBrush: FormatBrush,
|
||||
IconPreviewOpen: PreviewOpen,
|
||||
IconPreviewClose: PreviewClose,
|
||||
IconStopwatchStart: StopwatchStart,
|
||||
}
|
||||
|
||||
export default {
|
||||
|
@ -25,6 +25,11 @@
|
||||
@close="writingBoardToolVisible = false"
|
||||
/>
|
||||
|
||||
<CountdownTimer
|
||||
v-if="timerlVisible"
|
||||
@close="timerlVisible = false"
|
||||
/>
|
||||
|
||||
<div class="tools-left">
|
||||
<IconLeftTwo class="tool-btn" theme="two-tone" :fill="['#111', '#fff']" @click="execPrev()" />
|
||||
<IconRightTwo class="tool-btn" theme="two-tone" :fill="['#111', '#fff']" @click="execNext()" />
|
||||
@ -43,6 +48,9 @@
|
||||
<Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.3" title="激光笔">
|
||||
<IconMagic class="tool-btn" :class="{ 'active': laserPen }" @click="laserPen = !laserPen" />
|
||||
</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="演讲者视图">
|
||||
<IconListView class="tool-btn" @click="changeViewMode('presenter')" />
|
||||
</Tooltip>
|
||||
@ -72,6 +80,7 @@ import useFullscreen from './hooks/useFullscreen'
|
||||
import ScreenSlideList from './ScreenSlideList.vue'
|
||||
import SlideThumbnails from './SlideThumbnails.vue'
|
||||
import WritingBoardTool from './WritingBoardTool.vue'
|
||||
import CountdownTimer from './CountdownTimer.vue'
|
||||
|
||||
const props = defineProps({
|
||||
changeViewMode: {
|
||||
@ -104,6 +113,7 @@ const { fullscreenState, manualExitFullscreen } = useFullscreen()
|
||||
|
||||
const rightToolsVisible = ref(false)
|
||||
const writingBoardToolVisible = ref(false)
|
||||
const timerlVisible = ref(false)
|
||||
const slideThumbnailModelVisible = ref(false)
|
||||
const laserPen = ref(false)
|
||||
|
||||
|
204
src/views/Screen/CountdownTimer.vue
Normal file
204
src/views/Screen/CountdownTimer.vue
Normal 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>
|
@ -4,6 +4,7 @@
|
||||
<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': 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()">
|
||||
<IconOffScreenOne class="tool-icon" v-if="fullscreenState" />
|
||||
<IconOffScreenOne class="tool-icon" v-else />
|
||||
@ -38,6 +39,12 @@
|
||||
v-if="writingBoardToolVisible"
|
||||
@close="writingBoardToolVisible = false"
|
||||
/>
|
||||
|
||||
<CountdownTimer
|
||||
v-if="timerlVisible"
|
||||
:left="75"
|
||||
@close="timerlVisible = false"
|
||||
/>
|
||||
</div>
|
||||
<div class="thumbnails"
|
||||
ref="thumbnailsRef"
|
||||
@ -85,6 +92,7 @@ import useFullscreen from './hooks/useFullscreen'
|
||||
import ThumbnailSlide from '@/views/components/ThumbnailSlide/index.vue'
|
||||
import ScreenSlideList from './ScreenSlideList.vue'
|
||||
import WritingBoardTool from './WritingBoardTool.vue'
|
||||
import CountdownTimer from './CountdownTimer.vue'
|
||||
|
||||
const props = defineProps({
|
||||
changeViewMode: {
|
||||
@ -98,6 +106,7 @@ const { slides, slideIndex, viewportRatio, currentSlide } = storeToRefs(useSlide
|
||||
const slideListWrapRef = ref<HTMLElement>()
|
||||
const thumbnailsRef = ref<HTMLElement>()
|
||||
const writingBoardToolVisible = ref(false)
|
||||
const timerlVisible = ref(false)
|
||||
const laserPen = ref(false)
|
||||
|
||||
const {
|
||||
|
Loading…
x
Reference in New Issue
Block a user