Selaa lähdekoodia

fix: 选取文本闪动;初次进入编辑模式光标定位不准确;移动后更新整块区域内影响到的编辑区域;新增的文本区域直接进入编辑,无文本则删除;

wzl 1 vuosi sitten
vanhempi
commit
74bc484a3c

+ 2 - 1
packages/core/src/editor/add_text.js

@@ -54,6 +54,7 @@ export default class AddText {
     if (toolType === this._tool) return
     if (!toolType) {
       this.reset()
+      this.container.style.cursor = 'default'
       this.container.style.touchAction = ''
 
       this.container.removeEventListener('mousedown', this.onMousedown)
@@ -79,7 +80,7 @@ export default class AddText {
   }
 
   handleMouseDown (event) {
-    if (!event.target.className && !event.target.className.includes('contentContainer')) return
+    if ((!event.target.className && !event.target.className.includes('contentContainer')) || this.contentContainer.selectedFrameIndex) return
     const { x, y } = getAbsoluteCoordinate(this.container, event)
     this.initStartPoint = { x, y }
 

+ 32 - 2
packages/core/src/editor/content_container.js

@@ -16,6 +16,7 @@ export class ContentContainer {
     this.frameEditorList = [];
     this.tool = this.pageViewer.tool || ''
     this.color = this.pageViewer.color || ''
+    this._selectedFrameIndex = 0
 
     this.onHandleTool = this.handleTool.bind(this)
   }
@@ -62,6 +63,7 @@ export class ContentContainer {
         if (isTextPtr) {
           let frameEditor = new TextEditor({
             eventBus: this.eventBus,
+            contentContainer: this,
             container: this.contentContainer,
             pagePtr: this.pagePtr,
             editPagePtr: this.editPagePtr,
@@ -119,6 +121,7 @@ export class ContentContainer {
     }
   }
 
+  // 添加编辑区域
   async addTextEditor (data) {
     const { rect, fontData, alignType } = data
     const param = {
@@ -132,15 +135,42 @@ export class ContentContainer {
 
     let frameEditor = new TextEditor({
       eventBus: this.eventBus,
+      contentContainer: this,
       container: this.contentContainer,
       pagePtr: this.pagePtr,
       editPagePtr: this.editPagePtr,
       editAreaPtr,
-      editAreaIndex: this.frameEditorList.length + 1,
+      editAreaIndex: this.frameEditorList.length,
       viewport: this.viewport,
       scale: this.scale,
-      messageHandler: this.messageHandler
+      messageHandler: this.messageHandler,
+      newAdd: true
     })
     this.frameEditorList.push(frameEditor)
   }
+
+  // 更新大区域图片
+  updateSomeCanvas(area) {
+    this.frameEditorList.forEach(item => {
+      const frame = item.frame
+      if (frame.right < area.left || frame.left > area.right || frame.bottom < area.top || frame.top > area.bottom) {
+        // console.log(item.editAreaIndex, '没重叠')
+      } else {
+        // console.log(item.editAreaIndex, '重叠了!!')
+        item.updateCanvas()
+      }
+    })
+  }
+
+  get selectedFrameIndex () {
+    return this._selectedFrameIndex
+  }
+
+  set selectedFrameIndex (index) {
+    this._selectedFrameIndex = index
+  }
+
+  removeEditor (index) {
+    this.frameEditorList.splice(index, 1)
+  }
 }

+ 174 - 32
packages/core/src/editor/text_editor.js

@@ -5,6 +5,7 @@ import { MARGIN_DISTANCE } from '../../constants'
 export class TextEditor {
   constructor({
     eventBus,
+    contentContainer,
     container,
     pagePtr,
     editPagePtr,
@@ -12,9 +13,11 @@ export class TextEditor {
     editAreaIndex,
     viewport,
     scale,
-    messageHandler
+    messageHandler,
+    newAdd
    }) {
     this.eventBus = eventBus
+    this.contentContainer = contentContainer
     this.container = container
     this.pagePtr = pagePtr
     this.editPagePtr = editPagePtr
@@ -23,6 +26,7 @@ export class TextEditor {
     this.viewport = viewport
     this.scale = scale
     this.messageHandler = messageHandler
+    this.newAdd = newAdd
 
     this.frame = null
     this.canvas = null
@@ -112,13 +116,12 @@ export class TextEditor {
     )
     this.textContainer.append(textarea)
     this.textarea = textarea
-    let text = await this.messageHandler.sendWithPromise('GetText', this.editAreaPtr)
-    // console.log(text)
+    let text = await this.getText()
     this.textarea.innerHTML = text
 
     this.frameContainer.append(this.textContainer)
 
-    this.textContainer.addEventListener('click', this.onClick)
+    // this.textContainer.addEventListener('click', this.onClick)
     this.textContainer.addEventListener('mousedown', this.onMousedown)
     this.textContainer.addEventListener('mouseup', this.onMouseup)
 
@@ -274,6 +277,10 @@ export class TextEditor {
 
     this.outerLine.addEventListener('mousedown', this.onMousedown)
     this.outerLine.addEventListener('mouseup', this.onMouseup)
+
+    if (this.newAdd) {
+      this.goEditing()
+    }
   }
 
   getActualRect (viewport, s, frame) {
@@ -321,12 +328,12 @@ export class TextEditor {
     }
   }
   
-  getInitialPoint () {
+  getInitialPoint (start, end) {
     const s = this.scale
-    const startX = this.start.x
-    const startY = this.start.y
-    const endX = this.end.x
-    const endY = this.end.y
+    const startX = start.x
+    const startY = start.y
+    const endX = end.x
+    const endY = end.y
 
     const { width, height, rotation } = this.viewport;
     let x1, y1, x2, y2;
@@ -364,8 +371,6 @@ export class TextEditor {
   }
 
   handleMouseDown (e) {
-    // e.stopPropagation()
-
     if (this.state === 1) {
       const operatorId = e.target.getAttribute('data-id')
     
@@ -393,7 +398,16 @@ export class TextEditor {
   }
 
   async handleMouseUp (e) {
-    // e.stopPropagation()
+    if (e && e.target !== this.textarea && e.target !== this.textContainer) {
+      const newEvent = new MouseEvent('mouseup', {
+        offsetX: e.offsetX,
+        offsetY: e.offsetY,
+        type: e.type,
+        target: this.textarea
+      })
+      this.textarea.dispatchEvent(newEvent)
+    }
+
     if (!this.mouseDown) return
     this.mouseDown = false
 
@@ -405,11 +419,22 @@ export class TextEditor {
     if (this.state === 0) {
       this.state = 1
       flag = false
+
+      this.contentContainer.selectedFrameIndex = this.editAreaIndex + 1
     }
 
     if (this.state === 1) {
       this.frameContainer.classList.remove('editing')
 
+      const oldPoint = {
+        start: this.start,
+        end: this.end
+      }
+      const newPoint = {
+        start: this.newStart,
+        end: this.newEnd
+      }
+
       const { pageX, pageY } = getClickPoint(e)
       if (!(pageX === this.startState?.clickX && pageY === this.startState?.clickY) && this.newStart && this.newEnd) {
         this.start = this.newStart
@@ -419,7 +444,7 @@ export class TextEditor {
       this.container.append(this.outerLine)
 
       if (this.mouseMoved) {
-        const { start, end } = this.getInitialPoint()
+        const { start, end } = this.getInitialPoint(this.start, this.end)
         const rect = {
           left: start.x,
           top: start.y,
@@ -433,6 +458,8 @@ export class TextEditor {
           rect
         })
         this.updateCanvas()
+        const area = this.getEntireArea(oldPoint, newPoint)
+        this.contentContainer.updateSomeCanvas(area)
 
       } else if (!this.mouseMoved && flag) {
         this.state = 2
@@ -476,7 +503,8 @@ export class TextEditor {
       if (this.selectedRects && !this.mouseMoved) {
         this.clearSelectText()
       }
-      
+
+      this.textarea.focus()
       this.textarea.addEventListener('blur', this.onBlur)
       this.textarea.addEventListener('keydown', this.onKeydown)
       this.textarea.addEventListener('textInput', this.onTextInput)
@@ -648,7 +676,10 @@ export class TextEditor {
     // console.log('blur')
   }
 
-  handleOutside () {
+  async handleOutside () {
+    if (this.newAdd) {
+      this.state = 1
+    }
     this.state = this.state === 2 ? 1 : this.state === 1 ? 0 : this.state
 
     this.frameContainer.classList.remove('editing')
@@ -663,6 +694,8 @@ export class TextEditor {
       this.frameContainer.classList.remove('selected')
 
       this.outerLine.remove()
+
+      this.contentContainer.selectedFrameIndex = 0
     }
     
     if (this.state === 1) {
@@ -672,15 +705,20 @@ export class TextEditor {
     }
 
     this.textarea.removeEventListener('blur', this.onBlur)
-    this.textarea.removeEventListener('keyup', this.onKeydown)
+    this.textarea.removeEventListener('keydown', this.onKeydown)
     this.textarea.removeEventListener('textInput', this.onTextInput)
+
+    if (this.newAdd) {
+      const text = await this.getText()
+      if (text.length === 0) {
+        this.remove()
+      }
+    }
   }
 
   async handleKeyDown (e) {
-    // e.stopPropagation()
     let keyCode = e.keyCode || e.which
 
-    console.log(keyCode)
     let isToolKey = (keyCode === 9 || keyCode === 27 || keyCode === 20 || keyCode === 16 || keyCode === 17 || keyCode === 18 || keyCode === 91 || keyCode === 93 || keyCode === 37 || keyCode === 38 || keyCode === 39 || keyCode === 40)
     if (this.selectedCharRange && !isToolKey) {
       const newChar = await this.getCharPlace('DeleteChars')
@@ -715,8 +753,6 @@ export class TextEditor {
   }
 
   async handleTextInput (e) {
-    // e.stopPropagation()
-    // console.log(e.data)
     const newChar = await this.messageHandler.sendWithPromise('InsertText', {
       editAreaPtr: this.editAreaPtr,
       char: this.activeCharPlace,
@@ -729,6 +765,7 @@ export class TextEditor {
     this.updateCursorLine()
   }
 
+  // 更新canvas图
   async updateCanvas() {
     this.frame = await this.messageHandler.sendWithPromise('GetFrame', {
       pagePtr: this.pagePtr,
@@ -774,16 +811,23 @@ export class TextEditor {
       bottom: imgRect.bottom,
       top: imgRect.top
     })
+    this.imageArray = imageArray
 
     this.canvas.width = imgRect.width
     this.canvas.height = imgRect.height
 
+    this.drawCanvas()
+  }
+
+  // 绘制canvas
+  drawCanvas () {
     const ctx = this.canvas.getContext('2d')
-    const imageData = ctx.createImageData(imgRect.width, imgRect.height)
-    imageData.data.set(imageArray)
+    const imageData = ctx.createImageData(this.imgRect.width, this.imgRect.height)
+    imageData.data.set(this.imageArray)
     ctx.putImageData(imageData, 0, 0)
   }
 
+  // 更新光标位置
   async updateCursorLine () {
     let cursorPoints = await this.messageHandler.sendWithPromise('GetTextCursorPoints', {
       pagePtr: this.pagePtr,
@@ -806,6 +850,7 @@ export class TextEditor {
     this.cursor.append(cursorLine)
   }
 
+  // 某个操作之后,获取光标所在字符的位置
   async getCharPlace (action) {
     const data = {
       editAreaPtr: this.editAreaPtr,
@@ -820,6 +865,7 @@ export class TextEditor {
     return await this.messageHandler.sendWithPromise(action, data)
   }
 
+  // 根据定位点,获取选中区域的始末字符位置
   async getCharsRange (startPoint, endPoint) {
     return await this.messageHandler.sendWithPromise('SelectCharsRangeAtPos', {
       pagePtr: this.pagePtr,
@@ -829,10 +875,17 @@ export class TextEditor {
     })
   }
 
+  // 保存编辑
   saveEdit () {
     this.messageHandler.sendWithPromise('EndEdit', this.editPagePtr)
   }
 
+  // 获取区域内的文本
+  getText () {
+    return this.messageHandler.sendWithPromise('GetText', this.editAreaPtr)
+  }
+
+  // 更新outerline框的位置大小
   updateOutline ({ start, end }) {
     const rect = this.calculate(start, end)
 
@@ -870,6 +923,7 @@ export class TextEditor {
     this.topRect.setAttribute("x", (rect.width - this.pointWidth + this.borderWidth) / 2)
   }
 
+  // 获取选中区域里文本的矩形rect
   async getCharsRect (startPoint, endPoint) {
     if (
       startPoint.SectionIndex === endPoint.SectionIndex &&
@@ -914,6 +968,7 @@ export class TextEditor {
     return selectedRectList
   }
 
+  // 选中文本的渲染
   async selectText () {
     const { start, end } = this.selectedCharRange
     const selectedRectList = await this.getCharsRect(start, end)
@@ -956,7 +1011,7 @@ export class TextEditor {
           prevSelectedRect.height
         )
       }
-      await this.updateCanvas()
+      this.drawCanvas()
     }
   
     this.selectedRects = selectedRectList // store the new rectangles
@@ -972,10 +1027,10 @@ export class TextEditor {
         selectedRect.height
       )
     }
-    
     this.updateCursorLine()
   }
 
+  // 取消选中文本
   async clearSelectText () {
     if (this.selectedRects?.length) {
       for (const selectedRect of this.selectedRects) {
@@ -987,11 +1042,12 @@ export class TextEditor {
           selectedRect.height
         )
       }
-      await this.updateCanvas()
+      this.drawCanvas()
     }
     this.selectedRects = null
   }
 
+  // 属性面板 修改属性
   async handlePropertyPanelChanged (props) {
     if (this.state !== 2) return
     for (const item in props) {
@@ -1078,17 +1134,103 @@ export class TextEditor {
     this.selectText()
   }
 
+  // 十六进制颜色转换为rgb
   hexToRgb (hex) {
-    hex = hex.replace("#", "");
+    hex = hex.replace("#", "")
   
-    let shorthand = hex.length === 3;
+    let shorthand = hex.length === 3
   
-    hex = shorthand ? hex.repeat(2) : hex;
+    hex = shorthand ? hex.repeat(2) : hex
   
-    let r = parseInt(hex.substr(0, 2), 16);
-    let g = parseInt(hex.substr(2, 2), 16);
-    let b = parseInt(hex.substr(4, 2), 16);
+    let r = parseInt(hex.substr(0, 2), 16)
+    let g = parseInt(hex.substr(2, 2), 16)
+    let b = parseInt(hex.substr(4, 2), 16)
   
-    return {r, g, b};
+    return {r, g, b}
+  }
+
+  // 计算编辑区域移动后,影响的整个区域的位置
+  getEntireArea (oldPoint, newPoint) {
+    let oldP = this.getInitialPoint(oldPoint.start, oldPoint.end)
+    let newP = this.getInitialPoint(newPoint.start, newPoint.end)
+
+    let left = Math.min(oldP.start.x, newP.start.x)
+    let top = Math.min(oldP.start.y, newP.start.y)
+    let right = Math.max(oldP.end.x, newP.end.x)
+    let bottom = Math.max(oldP.end.y, newP.end.y)
+
+    const area = {
+      left,
+      top,
+      right,
+      bottom
+    }
+    return area
+  }
+
+  // 直接进入编辑模式
+  goEditing () {
+    this.state = 2
+    this.frameContainer.classList.add('selected')
+    this.frameContainer.classList.add('editing')
+
+    this.activeCharPlace = {
+      SectionIndex: 0,
+      LineIndex: 0,
+      RunIndex: 0,
+      CharIndex: -1
+    }
+
+    const cursor = createSvg(
+      'svg',
+      {
+        class: 'cursor-animation'
+      },
+      {
+        position: 'absolute',
+        left: 0,
+        top: 0,
+        width: '100%',
+        height: '100%',
+      }
+    )
+    this.cursor = cursor
+    this.textContainer.append(this.cursor)
+    
+    const cursorLine = createSvg(
+      'line',
+      {
+        x1: 0,
+        x2: 0,
+        y1: 0,
+        y2: 20,
+        stroke: 'currentcolor'
+      },
+    )
+    this.cursorLine = cursorLine
+    this.cursor.append(cursorLine)
+
+    this.textarea.focus()
+    this.textarea.addEventListener('blur', this.onBlur)
+    this.textarea.addEventListener('keydown', this.onKeydown)
+    this.textarea.addEventListener('textInput', this.onTextInput)
+
+    const editPanel = document.querySelector('.edit-panel')
+    onClickOutside([this.textContainer, this.outerLine, editPanel], this.handleOutside.bind(this))
+  }
+
+  async remove () {
+    this.frameContainer.remove()
+
+    await this.messageHandler.send('RemoveEditAreaByIndex', {
+      editPagePtr: this.editPagePtr,
+      editAreaIndex: this.editAreaIndex
+    })
+    // await this.messageHandler.send('RemoveEditArea', {
+    //   editPagePtr: this.editPagePtr,
+    //   editAreaPtr: this.editAreaPtr
+    // })
+    
+    this.contentContainer.removeEditor(this.editAreaIndex)
   }
 }

+ 16 - 0
packages/core/src/worker/compdfkit_worker.js

@@ -1092,6 +1092,22 @@ debugger
         alignType
       )
     })
+
+    messageHandler.on('RemoveEditAreaByIndex', (data) => {
+      const { editPagePtr, editAreaIndex } = data
+      Module._RemoveEditAreaByIndex(
+        editPagePtr,
+        editAreaIndex
+      )
+    })
+
+    messageHandler.on('RemoveEditArea', (data) => {
+      const { editPagePtr, editAreaPtr } = data
+      Module._RemoveEditArea(
+        editPagePtr,
+        editAreaPtr
+      )
+    })
   }
 
 }

BIN
packages/webview/public/example/Quick Start Guide for ComPDFKit Web Demo.pdf


BIN
packages/webview/public/example/test.pdf


+ 30 - 0
packages/webview/src/assets/main.scss

@@ -328,3 +328,33 @@ input, textarea, select {
     }
   }
 }
+
+.contentContainer > .outerline {
+  .move {
+    cursor: all-scroll;
+  }
+  .nw-resize {
+    cursor: nw-resize;
+  }
+  .w-resize {
+    cursor: w-resize;
+  }
+  .sw-resize {
+    cursor: sw-resize;
+  }
+  .s-resize {
+    cursor: s-resize;
+  }
+  .se-resize {
+    cursor: se-resize;
+  }
+  .e-resize {
+    cursor: e-resize;
+  }
+  .ne-resize {
+    cursor: ne-resize;
+  }
+  .n-resize {
+    cursor: n-resize;
+  }
+}

+ 2 - 2
packages/webview/src/components/DocumentContainer/DocumentContainer.vue

@@ -182,7 +182,7 @@
       toggleButton: document.querySelector('.toggle-button')
     })
     let initialDoc = getHashParameters('d', '');
-    initialDoc = initialDoc ? JSON.parse(initialDoc) : './example/test.pdf'
+    initialDoc = initialDoc ? JSON.parse(initialDoc) : './example/Quick Start Guide for ComPDFKit Web Demo.pdf'
     initialDoc = Array.isArray(initialDoc) ? initialDoc : [initialDoc]
     const activeTab = useViewer.activeTab || 0
     initialDoc = initialDoc[activeTab]
@@ -573,7 +573,7 @@
         }
       }
       &.selected {
-        z-index: 1;
+        z-index: 2;
       }
       &.editing {
         .text-container {