【PPTist】幻灯片放映

打印 上一主题 下一主题

主题 889|帖子 889|积分 2667


放映功能的代码都在 src/hooks/useScreening.ts,我们看一下 从当前页开始 放映的功能。
  1. // 进入放映状态(从当前页开始)
  2. const enterScreening = () => {
  3.   enterFullscreen()
  4.   screenStore.setScreening(true)
  5. }
复制代码
起首是 enterFullscreen(),进入全屏放映
src/utils/fullscreen.ts
  1. // 进入全屏
  2. export const enterFullscreen = () => {
  3.   const docElm = document.documentElement
  4.   if (docElm.requestFullscreen) docElm.requestFullscreen()
  5.   else if (docElm.mozRequestFullScreen) docElm.mozRequestFullScreen()
  6.   else if (docElm.webkitRequestFullScreen) docElm.webkitRequestFullScreen()
  7.   else if (docElm.msRequestFullscreen) docElm.msRequestFullscreen()
  8. }
复制代码
进入全屏指的是整个编辑器 document.documentElement 进入全屏

就先找一下当前的浏览器能用的全屏的方法,然后调用。
然后执行 screenStore.setScreening(true)
src/store/screen.ts
  1. setScreening(screening: boolean) {
  2.   this.screening = screening
  3. },
复制代码
然后通过这个属性控制的App..vue中的组件的显示
src/App.vue
  1. <template>
  2.   <Screen v-if="screening" />
  3.   <Editor v-else-if="_isPC" />
  4.   <Mobile v-else />
  5. </template>
复制代码
进入放映模式
src/views/Screen/index.vue
  1. <template>
  2.   <div class="pptist-screen">
  3.     <BaseView :changeViewMode="changeViewMode" v-if="viewMode === 'base'" />
  4.     <PresenterView :changeViewMode="changeViewMode" v-else-if="viewMode === 'presenter'" />
  5.   </div>
  6. </template>
复制代码
通过幻灯片放映的下拉框进入的是普通视图,就是 BaseView。然后右键有一个演讲者视图,就是 PresenterView

1、普通视图

src/views/Screen/BaseView.vue
右键点击工具栏的时候右下角会浮现工具栏

这内里的内容其实跟右键菜单项差不多
① 画笔工具

右键点击画笔工具时会出现画笔工具 src/views/Screen/WritingBoardTool.vue

利用画笔工具的时候,sizePopoverType 这个属性用来表现当前利用的是哪个工具。
绘制过程的组件 src/components/WritingBoard.vue
绘制过程分为三个阶段,鼠标落下、鼠标移动、鼠标抬起


  • mousedown
  1. // 处理鼠标(触摸)事件
  2. // 准备开始绘制/擦除墨迹(落笔)
  3. const handleMousedown = (e: MouseEvent | TouchEvent) => {
  4.   // 获取鼠标在canvas中的相对位置
  5.   const [mouseX, mouseY] = getMouseOffsetPosition(e)
  6.   // 计算鼠标在canvas中的绝对位置
  7.   const x = mouseX / widthScale.value
  8.   const y = mouseY / heightScale.value
  9.   // 设置鼠标状态
  10.   isMouseDown = true
  11.   lastPos = { x, y }
  12.   lastTime = new Date().getTime()
  13.   // 设置鼠标状态
  14.   if (!(e instanceof MouseEvent)) {
  15.     mouse.value = { x: mouseX, y: mouseY }
  16.     mouseInCanvas.value = true
  17.   }
  18. }
复制代码


  • mousemove
  1. // 开始绘制/擦除墨迹(移动)
  2. const handleMousemove = (e: MouseEvent | TouchEvent) => {
  3.   // 获取鼠标在canvas中的相对位置
  4.   const [mouseX, mouseY] = getMouseOffsetPosition(e)
  5.   // 计算鼠标在canvas中的绝对位置
  6.   const x = mouseX / widthScale.value
  7.   const y = mouseY / heightScale.value
  8.   // 设置鼠标位置
  9.   mouse.value = { x: mouseX, y: mouseY }
  10.   // 开始绘制/擦除墨迹
  11.   if (isMouseDown) handleMove(x, y)
  12. }
复制代码
  1. // 路径操作
  2. const handleMove = (x: number, y: number) => {
  3.   const time = new Date().getTime()
  4.   if (props.model === 'pen') {
  5.     const s = getDistance(x, y)
  6.     const t = time - lastTime
  7.     const lineWidth = getLineWidth(s, t)
  8.     draw(x, y, lineWidth)
  9.     lastLineWidth = lineWidth
  10.   }
  11.   else if (props.model === 'mark') draw(x, y, props.markSize)
  12.   else erase(x, y)
  13.   lastPos = { x, y }
  14.   lastTime = new Date().getTime()
  15. }
复制代码
此中画画、橡皮擦是通过 canvas 实现的
  1. // 绘制画笔墨迹方法
  2. const draw = (posX: number, posY: number, lineWidth: number) => {
  3.   if (!ctx) return
  4.   const lastPosX = lastPos.x
  5.   const lastPosY = lastPos.y
  6.   ctx.lineWidth = lineWidth
  7.   ctx.strokeStyle = props.color
  8.   ctx.beginPath()
  9.   ctx.moveTo(lastPosX, lastPosY)
  10.   ctx.lineTo(posX, posY)
  11.   ctx.stroke()
  12.   ctx.closePath()
  13. }
复制代码


  • mouseup
  1. // 结束绘制/擦除墨迹(停笔)
  2. const handleMouseup = () => {
  3.   if (!isMouseDown) return
  4.   isMouseDown = false
  5.   emit('end')
  6. }
复制代码
竣事绘制会被父组件监听
src/views/Screen/WritingBoardTool.vue
  1. // 每次绘制完成后将绘制完的图片更新到数据库
  2. const hanldeWritingEnd = () => {
  3.   const dataURL = writingBoardRef.value!.getImageDataURL()
  4.   if (!dataURL) return
  5.   db.writingBoardImgs.where('id').equals(currentSlide.value.id).toArray().then(ret => {
  6.     const currentImg = ret[0]
  7.     if (currentImg) db.writingBoardImgs.update(currentImg, { dataURL })
  8.     else db.writingBoardImgs.add({ id: currentSlide.value.id, dataURL })
  9.   })
  10. }
复制代码
将绘制的内容存储到数据库中,切换幻灯片的时候,会查察当前幻灯片有没有对应的绘制字迹
  1. // 打开画笔工具或切换页面时,将数据库中存储的墨迹绘制到画布上
  2. watch(currentSlide, () => {
  3.   db.writingBoardImgs.where('id').equals(currentSlide.value.id).toArray().then(ret => {
  4.     const currentImg = ret[0]
  5.     writingBoardRef.value!.setImageDataURL(currentImg?.dataURL || '')
  6.   })
  7. }, { immediate: true })
复制代码
② 自动放映

自动放映的方法在这里:src/views/Screen/hooks/useExecPlay.ts
  1. // 自动播放
  2. const autoPlayInterval = ref(2500)
  3. const autoPlay = () => {
  4.   closeAutoPlay()
  5.   message.success('开始自动放映')
  6.   autoPlayTimer.value = setInterval(execNext, autoPlayInterval.value)
  7. }
复制代码
execNext() 方法咱们以前见过,就是下一张的方法。可以看到自动放映就是每隔固定时间执行下一张的方法
③ 循环放映

  1. // 循环放映
  2. const loopPlay = ref(false)
  3. const setLoopPlay = (loop: boolean) => {
  4.   loopPlay.value = loop
  5. }
复制代码
循环播放通过 loopPlay 控制。
在执行 execNext() 方法的时候,如果执行到末了一张幻灯片,就会需要判定是否是循环放映模式,如果是,就将幻灯片从头开始,
  1. const execNext = () => {
  2.   if (formatedAnimations.value.length && animationIndex.value < formatedAnimations.value.length) {
  3.     runAnimation()
  4.   }
  5.   else if (slideIndex.value < slides.value.length - 1) {
  6.     slidesStore.updateSlideIndex(slideIndex.value + 1)
  7.     animationIndex.value = 0
  8.     inAnimation.value = false
  9.   }
  10.   else {
  11.     // 如果循环播放,则切换到第一页
  12.     if (loopPlay.value) turnSlideToIndex(0)
  13.     else {
  14.       throttleMassage('已经是最后一页了')
  15.       closeAutoPlay()
  16.     }
  17.     inAnimation.value = false
  18.   }
  19. }
复制代码
④ 查察所有幻灯片

点击“查察所有幻灯片”时,会显示 src/views/Screen/SlideThumbnails.vue 组件,页面就变成如许了

不过这页面可以看出来挺简朴的,点击某一张幻灯片的时候,会进入目标幻灯片的放映模式
  1. const turnSlide = (index: number) => {
  2.   props.turnSlideToIndex(index)
  3.   emit('close')
  4. }
复制代码
src/views/Screen/hooks/useExecPlay.ts
  1. // 切换幻灯片到指定的页面
  2. const turnSlideToIndex = (index: number) => {
  3.   slidesStore.updateSlideIndex(index)
  4.   animationIndex.value = 0
  5. }
复制代码
触发 close 方法,就是潜伏这个 SlideThumbnails 组件
  1. @close="slideThumbnailModelVisible = false"
复制代码
2、演讲者视图


① 画笔

画笔跟上面的画笔工具是一个组件,由此可知组件化的告急性,功能复用多么方便!

② 激光笔

设置成激光笔模式的时候,内容地域会增长一个类名
  1. <div
  2.   class="slide-list-wrap"
  3.   :class="{ 'laser-pen': laserPen }"
  4.   ref="slideListWrapRef"
  5. >
复制代码
是用来设置 cursor 属性的,显示成一个小圆点,是通过base64设置的,代码太长了我就不粘贴了

然后激光笔似乎没有别的作用了,就是让鼠标更明显一些。
③ 计时器

计时器会显示这么一个小组件

src/views/Screen/CountdownTimer.vue
表现分和秒的两个小框框是由不可编辑的 input 框组成
  1. <input
  2.   type="text"
  3.   :value="fillDigit(minute, 2)"
  4.   :maxlength="3" :disabled="inputEditable"
  5.   @mousedown.stop
  6.   @blur="$event => changeTime($event, 'minute')"
  7.   @keydown.stop
  8.   @keydown.enter.stop="$event => changeTime($event, 'minute')"
  9. >
复制代码
开始计时的时候,会设置计时器
  1. const start = () => {
  2.   clearTimer()
  3.   if (isCountdown.value) {
  4.     // 倒计时
  5.     timer.value = setInterval(() => {
  6.       time.value = time.value - 1
  7.       // 倒计时结束 重置计时器
  8.       if (time.value <= 0) reset()
  9.     }, 1000)
  10.   }
  11.   else {
  12.     // 计时
  13.     timer.value = setInterval(() => {
  14.       time.value = time.value + 1
  15.       // 计时超过36000秒 暂停计时器
  16.       if (time.value > 36000) pause()
  17.     }, 1000)
  18.   }
  19.   inTiming.value = true
  20. }
复制代码
至于分钟和秒数的盘算,都是根据 time 盘算出来的。
  1. const time = ref(0)
  2. const minute = computed(() => Math.floor(time.value / 60))
  3. const second = computed(() => time.value % 60)
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

钜形不锈钢水箱

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表