Browse Source

fix: Markup渲染优化,内容编辑时也渲染

wzl 3 months ago
parent
commit
90492e816c

+ 32 - 8
packages/core/src/annotation/layer.js

@@ -72,10 +72,13 @@ class ComPDFAnnotationLayer {
     this.onHandleToolMode = this.handleToolMode.bind(this)
     this.onHandlePropertyPanelChanged = this.handlePropertyPanelChanged.bind(this)
     this.onHandleAddSign = this.addSign.bind(this)
+    this.onRedrawMarkups = this.redrawMarkups.bind(this)
 
     this.annotationStore.$t = $t
 
     this.enableReply = enableReply
+    this.markupType = ['highlight', 'underline', 'squiggly', 'strikeout']
+    this.ctx = this.pageViewer.canvas.getContext('2d')
   }
 
   // 处理注释工具切换
@@ -93,17 +96,16 @@ class ComPDFAnnotationLayer {
       this.renderPaintScene()
     }
 
-    const markup = ['highlight', 'underline', 'squiggly', 'strikeout']
-    if (!tool || markup.includes(tool)) {
+    if (!tool || this.markupType.includes(tool)) {
       document.querySelector('.document').classList.remove('annotation-edit')
     }
 
-    if (markup.includes(tool) || tool === 'cropPage' || tool === 'selectText') {
+    if (this.markupType.includes(tool) || tool === 'cropPage' || tool === 'selectText') {
       this.resetManagers()
     }
 
     const shapes = ['line', 'arrow', 'circle', 'square', 'ink']
-    if (markup.includes(tool) && color) {
+    if (this.markupType.includes(tool) && color) {
       this.svgElement.classList.add('point-none')
     }
 
@@ -365,6 +367,7 @@ class ComPDFAnnotationLayer {
     this.eventBus._on('toolModeChanged', this.onHandleToolMode)
     this.eventBus._on('setDefaultSelect', this.onHandleDefaultSelect)
     this.eventBus._on('propertyPanelChanged', this.onHandlePropertyPanelChanged)
+    this.eventBus._on('redrawMarkups', this.onRedrawMarkups)
 
     if (viewport) {
       const clonedViewport = viewport.clone({ dontFlip: true });
@@ -385,8 +388,7 @@ class ComPDFAnnotationLayer {
       for (let i = 0; i < this.annotations.length; i++) {
         const annotation = this.annotations[i]
         if (!annotation) continue
-        const markupType = ['highlight', 'underline', 'squiggly', 'strikeout']
-        if (markupType.includes(annotation.type)) {
+        if (this.markupType.includes(annotation.type)) {
           const markup = new MarkupTextAnnotation({
             container: this.div,
             annotation,
@@ -668,6 +670,7 @@ class ComPDFAnnotationLayer {
     this.eventBus._off('toolModeChanged', this.onHandleToolMode)
     this.eventBus._off('setDefaultSelect', this.onHandleDefaultSelect)
     this.eventBus._off('propertyPanelChanged', this.onHandlePropertyPanelChanged)
+    this.eventBus._off('redrawMarkups', this.onRedrawMarkups)
 
     this.annotationStore.notFirstRender = true
   }
@@ -709,8 +712,7 @@ class ComPDFAnnotationLayer {
     }
 
     const enableReply = this.enableReply
-    const markupType = ['highlight', 'underline', 'squiggly', 'strikeout']
-    if (markupType.includes(annotation.type)) {
+    if (this.markupType.includes(annotation.type)) {
       const markup = new MarkupTextAnnotation({
         container: this.div,
         annotation,
@@ -973,6 +975,28 @@ class ComPDFAnnotationLayer {
     this.pageDiv.removeEventListener('click', this.onHandleAddSign)
     this.eventBus.dispatch('initCursorStyle')
   }
+
+  // 在canvas上重绘markup
+  redrawMarkups (data) {
+    const { pageIndex, afterContentEdit, rect } = data
+    if (pageIndex !== this.page) return
+
+    if (!afterContentEdit) {
+      const canvasWidth = Math.round(this.viewport.width * this.ratio)
+      const canvasHeight = Math.round(this.viewport.height * this.ratio)
+      this.ctx.clearRect(0, 0, canvasWidth, canvasHeight)
+      this.ctx.putImageData(this.pageViewer.baseImageData, 0, 0)
+    }
+
+    for (let i = 0; i < this.annotationsArray.length; i++) {
+      const annotObj = this.annotationsArray[i]
+      const { annotation, positionArray } = annotObj
+      if (!annotation.isDelete && this.markupType.includes(annotation.type)) {
+        const drawMethod = afterContentEdit ? annotObj.drawIntersectionalCanvas : annotObj.drawCanvas
+        drawMethod.call(annotObj, afterContentEdit ? rect : positionArray)
+      }
+    }
+  }
 }
 
 export default ComPDFAnnotationLayer;

+ 18 - 10
packages/core/src/editor/text_editor.js

@@ -1060,7 +1060,7 @@ export class TextEditor {
   }
 
   // 更新canvas图
-  async updateCanvas(whole, oldRect) {
+  async updateCanvas(whole, oldRect, noRedraw) {
     if (!this.removed) await this.getRect()
 
     if (this.frameContainer && this.outerLineContainer) {
@@ -1149,7 +1149,9 @@ export class TextEditor {
       this.canvas.height = imgRect.height
     }
 
-    let { imageArray } = await this.messageHandler.sendWithPromise('PushRenderTask', {
+    if (noRedraw) return
+
+    const { imageArray } = await this.messageHandler.sendWithPromise('PushRenderTask', {
       pagePtr: this.pagePtr,
       scale: this.scale * this.ratio,
       left: this.imgRect.left,
@@ -1164,16 +1166,22 @@ export class TextEditor {
   }
 
   // 绘制canvas
-  drawCanvas () {
+  drawCanvas (noRedraw = false) {
     if (!this.imgRect) {
-      this.updateCanvas()
+      this.updateCanvas(null, null, noRedraw)
       return
     }
-    if (!this.pageViewer.canvas) return
+    if (!this.pageViewer.canvas || noRedraw) return
     const pageCtx = this.pageViewer.canvas.getContext('2d')
     const imageData = pageCtx.createImageData(parseInt(this.imgRect.width), parseInt(this.imgRect.height))
     imageData.data.set(this.imageArray)
     pageCtx.putImageData(imageData, this.imgRect.left, this.imgRect.top)
+
+    this.eventBus.dispatch('redrawMarkups', {
+      pageIndex: this.pageViewer.pageIndex,
+      afterContentEdit: true,
+      rect: this.getEntireArea(this.oldRect, this.rect)
+    })
   }
 
   // 更新光标位置
@@ -1426,7 +1434,7 @@ export class TextEditor {
   
         ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
       }
-      this.drawCanvas()
+      this.drawCanvas(true)
     }
   
     this.selectedRects = selectedRectList
@@ -1534,9 +1542,9 @@ export class TextEditor {
         const fontStyle = props.fontStyle
 
         await this.setCharsFontStyle('ClearCharsFontBold')
-        await this.refreshSelecedRange()
+        await this.refreshSelectedRange()
         await this.setCharsFontStyle('ClearCharsFontItalic')
-        await this.refreshSelecedRange()
+        await this.refreshSelectedRange()
 
         if (fontStyle === 1) {
           await this.setCharsFontStyle('SetCharsFontBold')
@@ -1583,7 +1591,7 @@ export class TextEditor {
     await this.updateCanvas(null, true)
 
     if (this.selectedCharRange && this.state === 2) {
-      await this.refreshSelecedRange()
+      await this.refreshSelectedRange()
     }
     
     if (this.state === 2) {
@@ -1854,7 +1862,7 @@ export class TextEditor {
   }
 
   // 修改属性后,刷新选中范围的数据
-  async refreshSelecedRange() {
+  async refreshSelectedRange() {
     const charRange = this.selectedCharRange || this.entireCharRange
     const { start, end } = await this.messageHandler.sendWithPromise('RefreshRange', {
       editAreaPtr: this.editAreaPtr,

+ 0 - 1
packages/core/src/index.js

@@ -1773,7 +1773,6 @@ class ComPDFKitViewer {
         if (!annotations.length) {
           delete this.annotations[annotation.pageIndex]
         }
-        this.redactionList = this.redactionList.filter((item) => item.pageIndex !== annotation.pageIndex)
         return
       } else {
         if (!this.webviewerServer) {

+ 28 - 44
packages/core/src/markup/text_annotation.js

@@ -33,13 +33,11 @@ class TextAnnotation extends BaseAnnotation {
     this.outerLineContainer = null
     this.ratio = window.devicePixelRatio || 1
     this.ctx = this.pageViewer.canvas.getContext('2d', { willReadFrequently: true })
-    this.baseImageData = this.pageViewer.baseImageData
     this.lineWidth = 2 * this.scale * this.ratio
 
     this.onHandleClick = this.handleClick.bind(this)
     this.onDelete = this.handleDelete.bind(this)
     this.onOpenReply = this.openReply.bind(this)
-    this.onUpdateMarkup = this.updateMarkup.bind(this)
 
     this.render()
   }
@@ -59,8 +57,6 @@ class TextAnnotation extends BaseAnnotation {
     const quadPoints = annotation.quadPoints
     if (!quadPoints) return
 
-    this.eventBus._on('markupUpdate', this.onUpdateMarkup)
-
     let positionArray = []
     for (let i = 0; (i + 3) < quadPoints.length; i+=4) {
       const x1 = parseInt(quadPoints[i].PointX * scale, 10),
@@ -258,15 +254,6 @@ class TextAnnotation extends BaseAnnotation {
     this.handleOutside()
     this.markupContainer.remove()
 
-    this.eventBus._off('markupUpdate', this.onUpdateMarkup)
-    this.ctx.clearRect(this.rect.left * this.ratio, this.rect.top * this.ratio, this.rect.width * this.ratio, this.rect.height * this.ratio)
-    this.ctx.putImageData(this.baseImageData, 0, 0, this.rect.left * this.ratio, this.rect.top * this.ratio, Math.round(this.rect.width * this.ratio + this.lineWidth + 20), Math.round(this.rect.height * this.ratio + this.lineWidth + 20))
-    this.eventBus.dispatch('markupUpdate', {
-      name: this.annotation.name,
-      pageIndex: this.page,
-      rect: this.rect
-    })
-
     this.annotation.isDelete = true
     const annotationData = {
       type: 'delete',
@@ -281,6 +268,7 @@ class TextAnnotation extends BaseAnnotation {
       annotationData.type = 'empty'
     }
     this.eventBus.dispatch('annotationChange', annotationData)
+    this.layer.redrawMarkups({ pageIndex: this.page })
   }
 
   setCss (ele, cssText) {
@@ -317,44 +305,13 @@ class TextAnnotation extends BaseAnnotation {
     this.eventBus.dispatch('openAnnotationReply', this.annotation)
   }
 
-  updateMarkup (data) {
-    if (data.pageIndex !== this.page || data.name === this.annotation.name) return
-
-    const rect1 = this.rect
-    const rect2 = data.rect
-
-    if (rect1.left > rect2.left + rect2.width ||
-        rect1.left + rect1.width < rect2.left ||
-        rect1.top > rect2.top + rect2.height ||
-        rect1.top + rect1.height < rect2.top
-    ) return
-
-    this.ctx.clearRect(this.rect.left * this.ratio, this.rect.top * this.ratio, this.rect.width * this.ratio, this.rect.height * this.ratio)
-    this.ctx.putImageData(this.baseImageData, 0, 0, this.rect.left * this.ratio, this.rect.top * this.ratio, Math.round(this.rect.width * this.ratio + this.lineWidth + 20), Math.round(this.rect.height * this.ratio + this.lineWidth + 20))
-    this.drawCanvas(this.positionArray)
-  }
-
   drawCanvas(positionArray) {
     const annotation = this.annotation
     if (['highlight', 'underline', 'strikeout'].includes(annotation.type)) {
       const { R, G, B } = convertColorToRGB(annotation.color)
       this.ctx.fillStyle = `rgba(${R}, ${G}, ${B}, ${annotation.opacity || 1})`
-
-      const markupContainer = document.createElement('div')
-      markupContainer.className = 'markup'
-
       for (let i = 0; i < positionArray.length; i++) {
         const position = positionArray[i]
-        const container = document.createElement('div')
-        this.setCss(container, {
-          position: 'absolute',
-          top: position.top + 'px',
-          left: position.left + 'px',
-          width: position.width + 'px',
-          height: position.height + 'px'
-        })
-        markupContainer.append(container)
-
         if (annotation.type === 'highlight') {
           this.ctx.fillRect(position.left * this.ratio, position.top * this.ratio, position.width * this.ratio, position.height * this.ratio)
         } else if (annotation.type === 'underline') {
@@ -381,6 +338,33 @@ class TextAnnotation extends BaseAnnotation {
       }
     }
   }
+
+  // 重绘交集区域
+  drawIntersectionalCanvas(rect) {
+    const intersectionArray = this.positionArray
+      .map(posRect => this.getIntersection(posRect, rect))
+      .filter(intersection => intersection)
+    this.drawCanvas(intersectionArray)
+  }
+
+  // 获取两个rect交集区域
+  getIntersection(rectA, rectB) {
+    const left = Math.max(rectA.left, rectB.left)
+    const right = Math.min(rectA.left + rectA.width, rectB.right)
+    const top = Math.max(rectA.top, rectB.top)
+    const bottom = Math.min(rectA.top + rectA.height, rectB.bottom)
+
+    if (right > left && bottom > top) {
+      return {
+        left,
+        top,
+        width: right - left,
+        height: bottom - top
+      }
+    }
+
+    return null
+  }
 }
 
 export default TextAnnotation