【PPTist】查找替换、绘制文本框

一、查找、替换

查找替换的组件 src/views/Editor/SearchPanel.vue

回车的时候会执行查找,查找替换功能的相关方法和属性都在 src/hooks/useSearch.ts 中。

1、查找

查找的方法也比较的朴实无华,就是 for 循环所有的幻灯片中的所有元素,通过 .match() 方法匹配要查找的关键词,如果匹配上的话,就放到 searchResults 中,会保存元素的id、元素类型以及元素所在的幻灯片的id。然后对 searchResults 中的数据进行高亮。

高亮的方法是会给目标文本创建一个父级 marker

mark 上还增加了一个 data-index 表示顺序

通过 textNode.parentNode!.replaceChild(mark, textNode) 使用 mark 替换文本元素

2、下一个/上一个

src/hooks/useSearch.ts 中有一个属性 searchIndex 来标识当前查找第几个关键词。下一个/上一个 的时候,会修改这个属性,然后执行 turnTarget() 方法

typescript 复制代码
const turnTarget = () => {
  if (searchIndex.value === -1) return
  
  const target = searchResults.value[searchIndex.value]

  if (target.slideId === currentSlide.value.id) setTimeout(setActiveMark, 0)
  else {
    const index = slides.value.findIndex(slide => slide.id === target.slideId)
    if (index !== -1) slidesStore.updateSlideIndex(index)
  }
}

这里有两种情况了,

  • 如果幻灯片没变化,执行 setActiveMark(),找对应的 mark,增加 active 类名
typescript 复制代码
const setActiveMark = () => {
  const markNodes = document.querySelectorAll('mark[data-index]')
  for (const node of markNodes) {
    setTimeout(() => {
      const index = (node as HTMLElement).dataset.index
      if (index !== undefined && +index === searchIndex.value) {
        node.classList.add('active')
      }
      else node.classList.remove('active')
    }, 0)
  }
}
  • 幻灯片变化,执行 slidesStore.updateSlideIndex(index) ,要跳转幻灯片。跳转完了之后呢,这里有一个监听幻灯片变化的方法,重新计算一下需要高亮的文本,设置 markactive
  • src/hooks/useSearch.ts
typescript 复制代码
watch(slideIndex, () => {
  nextTick(() => {
    highlightCurrentSlide()
    setTimeout(setActiveMark, 0)
  })
})
3、替换

替换的时候,如果没有搜索结果的话,会直接执行 searchNext() 方法。

如果当前有搜索结果,就找当前处在激活状态中的 mark

先制造一个 fakeElement,通过 parentNode.replaceChild(document.createTextNode(replaceWord.value), mark) 方法替换节点,

然后还通过 slidesStore.updateElement({ id: target.elId, props }) 更新元素,使用 fakeElement 替换原来的元素。

4、替换全部

替换全部的时候其实跟替换差不多,但是循环 searchResults,遍历所有的文本节点,给所有的 mark 都创造一个 fakeElement,使用 slidesStore.updateElement({ id: target.elId, slideId: target.slideId, props }) 进行更新。

创建一个fakeElement 而不是直接更新 DOM ,可以防止后续错误导致流程失败结果已经更新了DOM的情况,以及减少页面重绘次数,另外可以统一将更新操作统一执行,代码可维护性更高。

二、绘制文本框

绘制文字范围、绘制形状范围使用的都是 mainStore.setCreatingElement() 方法。这个方法接收一个对象作为参数,就是要绘制的元素本身。如果是绘制文字的话,

typescript 复制代码
// 绘制文字范围
const drawText = (vertical = false) => {
  mainStore.setCreatingElement({
    type: 'text',
    vertical,
  })
}

第二个参数表示是否是垂直方向,默认是横向。

src/views/Editor/Canvas/index.vue

typescript 复制代码
<ElementCreateSelection
   v-if="creatingElement"
   @created="data => insertElementFromCreateSelection(data)"
 />

有了 creatingElement,就会创建 ElementCreateSelection 组件,同时会执行 insertElementFromCreateSelection()

方法。

typescript 复制代码
  // 根据鼠标选区的位置大小插入元素
const insertElementFromCreateSelection = (selectionData: CreateElementSelectionData) => {
  if (!creatingElement.value) return

  const type = creatingElement.value.type
  if (type === 'text') {
    const position = formatCreateSelection(selectionData)
    position && createTextElement(position, { vertical: creatingElement.value.vertical })
  }
  mainStore.setCreatingElement(null)
}

然而,这里有一个容易混淆的地方,@created 并不是Vue3内置的函数,而是这个自定义组件自己定义的监听函数🥹🥹🥹,我说呢,这个方法的执行实际不太对啊,这个方法是鼠标起来的时候才会执行,而不是创建这个组件的时候执行。

我们看一下 src/views/Editor/Canvas/ElementCreateSelection.vue 组件里面的执行流程

  • mousedown

    鼠标落下的时候,执行 createSelection(),记录此时的坐标到 start 中。此时会添加 document.onmousemove 监听函数

  • mousemove

    鼠标移动的时候,实时计算坐标,记录到 end

  • mouseup

    鼠标抬起时,清空 onmousemoveonmouseup

    通过 e.button 判断此时有没有按鼠标右键

    typescript 复制代码
    e.button = 0  // 鼠标左键
    e.button = 1  // 鼠标中键(滚轮)
    e.button = 2  // 鼠标右键
    e.button = 3  // 浏览器后退键
    e.button = 4  // 浏览器前进键

    如果按了就表示取消绘制,mainStore.setCreatingElement(null)creatingElement 清空。

    否则就触发 created 方法,将 startend 传进去,然后自定义组件就会监听到。那我就不明白了,明明是组件内部会触发这个方法,干嘛还要写成监听函数的形式,直接写成组件里面的方法不就行了吗?一般定义监听函数,都是给父组件来触发的👽👽👽

    哦,知道了,因为这个

    typescript 复制代码
    const { insertElementFromCreateSelection, formatCreateSelection } = useInsertFromCreateSelection(viewportRef)

    要将 viewportRef 传进去呢,这个模版元素只能从父组件中传过去。

  • @created

    监听到这个方法之后,就要执行 insertElementFromCreateSelection(data)data 是位置信息,就是上面说的 startend

  • createTextElement()

    根据位置信息创建文本元素。这个方法以前也见过了。
    src/hooks/useCreateElement.ts

    typescript 复制代码
      // 创建(插入)一个元素并将其设置为被选中元素
      const createElement = (element: PPTElement, callback?: () => void) => {
        // 添加元素到元素列表
        slidesStore.addElement(element)
        // 设置被选中元素列表
        mainStore.setActiveElementIdList([element.id])
    
        if (creatingElement.value) mainStore.setCreatingElement(null)
    
        setTimeout(() => {
          // 设置编辑器区域为聚焦状态
          mainStore.setEditorareaFocus(true)
        }, 0)
    
        if (callback) callback()
    
        // 添加历史快照
        addHistorySnapshot()
      }
相关推荐
s_little_monster18 分钟前
【Linux】进程地址空间
linux·运维·服务器·经验分享·笔记·学习·学习方法
Orange30151128 分钟前
ES6~ES11新特性全解析
java·前端·javascript·es6
敲敲敲敲暴你脑袋37 分钟前
vue3中ref响应式变量为什么script中要用.value,而template模板中不需?
javascript·vue.js·visual studio code
Lanwarf-前端开发1 小时前
gis风场加载
开发语言·前端·javascript
海上彼尚1 小时前
ModuleJS 与 CommonJS 混用的两种解决方案
javascript·node.js
吃蛋糕的居居1 小时前
疯狂前端面试题(四)
前端·javascript·chrome·ajax·正则表达式·firefox·html5
程序员林北北1 小时前
【Golang学习之旅】gRPC 与 REST API 的对比及应用
java·开发语言·后端·学习·云原生·golang
蝈蝈ly2 小时前
WPF学习
学习·wpf
Lovely Ruby2 小时前
【ThreeJS Basics 1-3】Hello ThreeJS,实现第一个场景
javascript
L_Whhjjj3 小时前
c语言函数学习
c语言·学习