【PPTist】幻灯片放映
放映功能的代码都在 src/hooks/useScreening.ts,我们看一下 从当前页开始 放映的功能。
// 进入放映状态(从当前页开始)
const enterScreening = () => {
enterFullscreen()
screenStore.setScreening(true)
}
首先是 enterFullscreen()
,进入全屏放映
src/utils/fullscreen.ts
// 进入全屏
export const enterFullscreen = () => {
const docElm = document.documentElement
if (docElm.requestFullscreen) docElm.requestFullscreen()
else if (docElm.mozRequestFullScreen) docElm.mozRequestFullScreen()
else if (docElm.webkitRequestFullScreen) docElm.webkitRequestFullScreen()
else if (docElm.msRequestFullscreen) docElm.msRequestFullscreen()
}
进入全屏指的是整个编辑器 document.documentElement
进入全屏
就先找一下当前的浏览器能用的全屏的方法,然后调用。
然后执行 screenStore.setScreening(true)
src/store/screen.ts
setScreening(screening: boolean) {
this.screening = screening
},
然后通过这个属性控制的App..vue
中的组件的显示
src/App.vue
<template>
<Screen v-if="screening" />
<Editor v-else-if="_isPC" />
<Mobile v-else />
</template>
进入放映模式
src/views/Screen/index.vue
<template>
<div class="pptist-screen">
<BaseView :changeViewMode="changeViewMode" v-if="viewMode === 'base'" />
<PresenterView :changeViewMode="changeViewMode" v-else-if="viewMode === 'presenter'" />
</div>
</template>
通过幻灯片放映的下拉框进入的是普通视图,就是 BaseView
。然后右键有一个演讲者视图,就是 PresenterView
1、普通视图
src/views/Screen/BaseView.vue
右键点击工具栏的时候右下角会浮现工具栏
这里面的内容其实跟右键菜单项差不多
① 画笔工具
右键点击画笔工具时会出现画笔工具 src/views/Screen/WritingBoardTool.vue
使用画笔工具的时候,sizePopoverType 这个属性用来表示当前使用的是哪个工具。
绘制过程的组件 src/components/WritingBoard.vue,
绘制过程分为三个阶段,鼠标落下、鼠标移动、鼠标抬起
- mousedown
// 处理鼠标(触摸)事件
// 准备开始绘制/擦除墨迹(落笔)
const handleMousedown = (e: MouseEvent | TouchEvent) => {
// 获取鼠标在canvas中的相对位置
const [mouseX, mouseY] = getMouseOffsetPosition(e)
// 计算鼠标在canvas中的绝对位置
const x = mouseX / widthScale.value
const y = mouseY / heightScale.value
// 设置鼠标状态
isMouseDown = true
lastPos = { x, y }
lastTime = new Date().getTime()
// 设置鼠标状态
if (!(e instanceof MouseEvent)) {
mouse.value = { x: mouseX, y: mouseY }
mouseInCanvas.value = true
}
}
- mousemove
// 开始绘制/擦除墨迹(移动)
const handleMousemove = (e: MouseEvent | TouchEvent) => {
// 获取鼠标在canvas中的相对位置
const [mouseX, mouseY] = getMouseOffsetPosition(e)
// 计算鼠标在canvas中的绝对位置
const x = mouseX / widthScale.value
const y = mouseY / heightScale.value
// 设置鼠标位置
mouse.value = { x: mouseX, y: mouseY }
// 开始绘制/擦除墨迹
if (isMouseDown) handleMove(x, y)
}
// 路径操作
const handleMove = (x: number, y: number) => {
const time = new Date().getTime()
if (props.model === 'pen') {
const s = getDistance(x, y)
const t = time - lastTime
const lineWidth = getLineWidth(s, t)
draw(x, y, lineWidth)
lastLineWidth = lineWidth
}
else if (props.model === 'mark') draw(x, y, props.markSize)
else erase(x, y)
lastPos = { x, y }
lastTime = new Date().getTime()
}
其中画画、橡皮擦是通过 canvas
实现的
// 绘制画笔墨迹方法
const draw = (posX: number, posY: number, lineWidth: number) => {
if (!ctx) return
const lastPosX = lastPos.x
const lastPosY = lastPos.y
ctx.lineWidth = lineWidth
ctx.strokeStyle = props.color
ctx.beginPath()
ctx.moveTo(lastPosX, lastPosY)
ctx.lineTo(posX, posY)
ctx.stroke()
ctx.closePath()
}
- mouseup
// 结束绘制/擦除墨迹(停笔)
const handleMouseup = () => {
if (!isMouseDown) return
isMouseDown = false
emit('end')
}
结束绘制会被父组件监听
src/views/Screen/WritingBoardTool.vue
// 每次绘制完成后将绘制完的图片更新到数据库
const hanldeWritingEnd = () => {
const dataURL = writingBoardRef.value!.getImageDataURL()
if (!dataURL) return
db.writingBoardImgs.where('id').equals(currentSlide.value.id).toArray().then(ret => {
const currentImg = ret[0]
if (currentImg) db.writingBoardImgs.update(currentImg, { dataURL })
else db.writingBoardImgs.add({ id: currentSlide.value.id, dataURL })
})
}
将绘制的内容存储到数据库中,切换幻灯片的时候,会查看当前幻灯片有没有对应的绘制笔迹
// 打开画笔工具或切换页面时,将数据库中存储的墨迹绘制到画布上
watch(currentSlide, () => {
db.writingBoardImgs.where('id').equals(currentSlide.value.id).toArray().then(ret => {
const currentImg = ret[0]
writingBoardRef.value!.setImageDataURL(currentImg?.dataURL || '')
})
}, { immediate: true })
② 自动放映
自动放映的方法在这里:src/views/Screen/hooks/useExecPlay.ts
// 自动播放
const autoPlayInterval = ref(2500)
const autoPlay = () => {
closeAutoPlay()
message.success('开始自动放映')
autoPlayTimer.value = setInterval(execNext, autoPlayInterval.value)
}
execNext() 方法咱们以前见过,就是下一张的方法。可以看到自动放映就是每隔固定时间执行下一张的方法
③ 循环放映
// 循环放映
const loopPlay = ref(false)
const setLoopPlay = (loop: boolean) => {
loopPlay.value = loop
}
循环播放通过 loopPlay
控制。
在执行 execNext()
方法的时候,如果执行到最后一张幻灯片,就会需要判断是否是循环放映模式,如果是,就将幻灯片从头开始,
const execNext = () => {
if (formatedAnimations.value.length && animationIndex.value < formatedAnimations.value.length) {
runAnimation()
}
else if (slideIndex.value < slides.value.length - 1) {
slidesStore.updateSlideIndex(slideIndex.value + 1)
animationIndex.value = 0
inAnimation.value = false
}
else {
// 如果循环播放,则切换到第一页
if (loopPlay.value) turnSlideToIndex(0)
else {
throttleMassage('已经是最后一页了')
closeAutoPlay()
}
inAnimation.value = false
}
}
④ 查看所有幻灯片
点击“查看所有幻灯片”时,会显示 src/views/Screen/SlideThumbnails.vue 组件,页面就变成这样了
不过这页面可以看出来挺简单的,点击某一张幻灯片的时候,会进入目标幻灯片的放映模式
const turnSlide = (index: number) => {
props.turnSlideToIndex(index)
emit('close')
}
src/views/Screen/hooks/useExecPlay.ts
// 切换幻灯片到指定的页面
const turnSlideToIndex = (index: number) => {
slidesStore.updateSlideIndex(index)
animationIndex.value = 0
}
触发 close
方法,就是隐藏这个 SlideThumbnails 组件
@close="slideThumbnailModelVisible = false"
2、演讲者视图
① 画笔
画笔跟上面的画笔工具是一个组件,由此可知组件化的重要性,功能复用多么方便!
② 激光笔
设置成激光笔模式的时候,内容区域会增加一个类名
<div
class="slide-list-wrap"
:class="{ 'laser-pen': laserPen }"
ref="slideListWrapRef"
>
是用来设置 cursor
属性的,显示成一个小圆点,是通过base64设置的,代码太长了我就不粘贴了
然后激光笔好像没有别的作用了,就是让鼠标更明显一些。
③ 计时器
计时器会显示这么一个小组件
src/views/Screen/CountdownTimer.vue
表示分和秒的两个小框框是由不可编辑的 input
框组成
<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')"
>
开始计时的时候,会设置计时器
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
// 计时超过36000秒 暂停计时器
if (time.value > 36000) pause()
}, 1000)
}
inTiming.value = true
}
至于分钟和秒数的计算,都是根据 time
计算出来的。
const time = ref(0)
const minute = computed(() => Math.floor(time.value / 60))
const second = computed(() => time.value % 60)
原文地址:https://blog.csdn.net/weixin_45855469/article/details/145061165
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!