feat:optimize rich text render performance
This commit is contained in:
@@ -767,6 +767,13 @@ export class Draw {
|
|||||||
return rowList
|
return rowList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _drawRichText(ctx: CanvasRenderingContext2D) {
|
||||||
|
this.underline.render(ctx)
|
||||||
|
this.strikeout.render(ctx)
|
||||||
|
this.highlight.render(ctx)
|
||||||
|
this.textParticle.complete()
|
||||||
|
}
|
||||||
|
|
||||||
private _drawRow(ctx: CanvasRenderingContext2D, payload: IDrawRowPayload): IDrawRowResult {
|
private _drawRow(ctx: CanvasRenderingContext2D, payload: IDrawRowPayload): IDrawRowResult {
|
||||||
const { positionList, rowList, pageNo, startX, startY, startIndex, innerWidth } = payload
|
const { positionList, rowList, pageNo, startX, startY, startIndex, innerWidth } = payload
|
||||||
const { scale, tdPadding } = this.options
|
const { scale, tdPadding } = this.options
|
||||||
@@ -823,10 +830,10 @@ export class Draw {
|
|||||||
positionList.push(positionItem)
|
positionList.push(positionItem)
|
||||||
// 元素绘制
|
// 元素绘制
|
||||||
if (element.type === ElementType.IMAGE) {
|
if (element.type === ElementType.IMAGE) {
|
||||||
this.textParticle.complete()
|
this._drawRichText(ctx)
|
||||||
this.imageParticle.render(ctx, element, x, y + offsetY)
|
this.imageParticle.render(ctx, element, x, y + offsetY)
|
||||||
} else if (element.type === ElementType.LATEX) {
|
} else if (element.type === ElementType.LATEX) {
|
||||||
this.textParticle.complete()
|
this._drawRichText(ctx)
|
||||||
this.laTexParticle.render(ctx, element, x, y + offsetY)
|
this.laTexParticle.render(ctx, element, x, y + offsetY)
|
||||||
} else if (element.type === ElementType.TABLE) {
|
} else if (element.type === ElementType.TABLE) {
|
||||||
if (isCrossRowCol) {
|
if (isCrossRowCol) {
|
||||||
@@ -836,16 +843,16 @@ export class Draw {
|
|||||||
}
|
}
|
||||||
this.tableParticle.render(ctx, element, x, y)
|
this.tableParticle.render(ctx, element, x, y)
|
||||||
} else if (element.type === ElementType.HYPERLINK) {
|
} else if (element.type === ElementType.HYPERLINK) {
|
||||||
this.textParticle.complete()
|
this._drawRichText(ctx)
|
||||||
this.hyperlinkParticle.render(ctx, element, x, y + offsetY)
|
this.hyperlinkParticle.render(ctx, element, x, y + offsetY)
|
||||||
} else if (element.type === ElementType.DATE) {
|
} else if (element.type === ElementType.DATE) {
|
||||||
this.textParticle.complete()
|
this._drawRichText(ctx)
|
||||||
this.dateParticle.render(ctx, element, x, y + offsetY)
|
this.dateParticle.render(ctx, element, x, y + offsetY)
|
||||||
} else if (element.type === ElementType.SUPERSCRIPT) {
|
} else if (element.type === ElementType.SUPERSCRIPT) {
|
||||||
this.textParticle.complete()
|
this._drawRichText(ctx)
|
||||||
this.superscriptParticle.render(ctx, element, x, y + offsetY)
|
this.superscriptParticle.render(ctx, element, x, y + offsetY)
|
||||||
} else if (element.type === ElementType.SUBSCRIPT) {
|
} else if (element.type === ElementType.SUBSCRIPT) {
|
||||||
this.textParticle.complete()
|
this._drawRichText(ctx)
|
||||||
this.subscriptParticle.render(ctx, element, x, y + offsetY)
|
this.subscriptParticle.render(ctx, element, x, y + offsetY)
|
||||||
} else if (element.type === ElementType.SEPARATOR) {
|
} else if (element.type === ElementType.SEPARATOR) {
|
||||||
this.separatorParticle.render(ctx, element, x, y)
|
this.separatorParticle.render(ctx, element, x, y)
|
||||||
@@ -857,24 +864,24 @@ export class Draw {
|
|||||||
element.type === ElementType.CHECKBOX ||
|
element.type === ElementType.CHECKBOX ||
|
||||||
element.controlComponent === ControlComponent.CHECKBOX
|
element.controlComponent === ControlComponent.CHECKBOX
|
||||||
) {
|
) {
|
||||||
this.textParticle.complete()
|
this._drawRichText(ctx)
|
||||||
this.checkboxParticle.render(ctx, element, x, y + offsetY)
|
this.checkboxParticle.render(ctx, element, x, y + offsetY)
|
||||||
} else if (element.type === ElementType.TAB) {
|
} else if (element.type === ElementType.TAB) {
|
||||||
this.textParticle.complete()
|
this._drawRichText(ctx)
|
||||||
} else {
|
} else {
|
||||||
this.textParticle.record(ctx, element, x, y + offsetY)
|
this.textParticle.record(ctx, element, x, y + offsetY)
|
||||||
}
|
}
|
||||||
// 下划线绘制
|
// 下划线记录
|
||||||
if (element.underline) {
|
if (element.underline) {
|
||||||
this.underline.render(ctx, element.color!, x, y + curRow.height, metrics.width)
|
this.underline.recordFillInfo(ctx, x, y + curRow.height, metrics.width, 0, element.color)
|
||||||
}
|
}
|
||||||
// 删除线绘制
|
// 删除线记录
|
||||||
if (element.strikeout) {
|
if (element.strikeout) {
|
||||||
this.strikeout.render(ctx, x, y + curRow.height / 2, metrics.width)
|
this.strikeout.recordFillInfo(ctx, x, y + curRow.height / 2, metrics.width)
|
||||||
}
|
}
|
||||||
// 元素高亮
|
// 元素高亮记录
|
||||||
if (element.highlight) {
|
if (element.highlight) {
|
||||||
this.highlight.render(ctx, element.highlight, x, y, metrics.width, curRow.height)
|
this.highlight.recordFillInfo(ctx, x, y, metrics.width, curRow.height, element.highlight)
|
||||||
}
|
}
|
||||||
// 选区记录
|
// 选区记录
|
||||||
const { startIndex, endIndex } = this.range.getRange()
|
const { startIndex, endIndex } = this.range.getRange()
|
||||||
@@ -937,7 +944,8 @@ export class Draw {
|
|||||||
y = tablePreY
|
y = tablePreY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.textParticle.complete()
|
// 绘制富文本及文字
|
||||||
|
this._drawRichText(ctx)
|
||||||
// 绘制选区
|
// 绘制选区
|
||||||
if (rangeRecord.width && rangeRecord.height) {
|
if (rangeRecord.width && rangeRecord.height) {
|
||||||
const { x, y, width, height } = rangeRecord
|
const { x, y, width, height } = rangeRecord
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { IElementFillRect } from '../../../interface/Element'
|
||||||
|
|
||||||
|
export abstract class AbstractRichText {
|
||||||
|
public fillRect: IElementFillRect
|
||||||
|
public fillColor?: string
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.fillRect = this.clearFillInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
public clearFillInfo() {
|
||||||
|
this.fillColor = undefined
|
||||||
|
return this.fillRect = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 0,
|
||||||
|
height: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public recordFillInfo(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height?: number, color?: string) {
|
||||||
|
const isFirstRecord = !this.fillRect.width
|
||||||
|
if (!isFirstRecord && this.fillColor && this.fillColor !== color) {
|
||||||
|
this.render(ctx)
|
||||||
|
this.clearFillInfo()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (isFirstRecord) {
|
||||||
|
this.fillRect.x = x
|
||||||
|
this.fillRect.y = y
|
||||||
|
}
|
||||||
|
if (height && this.fillRect.height < height) {
|
||||||
|
this.fillRect.height = height
|
||||||
|
}
|
||||||
|
this.fillRect.width += width
|
||||||
|
this.fillColor = color
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract render(ctx: CanvasRenderingContext2D): void
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,21 +1,26 @@
|
|||||||
|
import { AbstractRichText } from './AbstractRichText'
|
||||||
import { IEditorOption } from '../../../interface/Editor'
|
import { IEditorOption } from '../../../interface/Editor'
|
||||||
import { Draw } from '../Draw'
|
import { Draw } from '../Draw'
|
||||||
|
|
||||||
export class Highlight {
|
export class Highlight extends AbstractRichText {
|
||||||
|
|
||||||
private options: Required<IEditorOption>
|
private options: Required<IEditorOption>
|
||||||
|
|
||||||
constructor(draw: Draw) {
|
constructor(draw: Draw) {
|
||||||
|
super()
|
||||||
this.options = draw.getOptions()
|
this.options = draw.getOptions()
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(ctx: CanvasRenderingContext2D, color: string, x: number, y: number, width: number, height: number) {
|
public render(ctx: CanvasRenderingContext2D) {
|
||||||
|
if (!this.fillRect.width) return
|
||||||
const { highlightAlpha } = this.options
|
const { highlightAlpha } = this.options
|
||||||
|
const { x, y, width, height } = this.fillRect
|
||||||
ctx.save()
|
ctx.save()
|
||||||
ctx.globalAlpha = highlightAlpha
|
ctx.globalAlpha = highlightAlpha
|
||||||
ctx.fillStyle = color
|
ctx.fillStyle = this.fillColor!
|
||||||
ctx.fillRect(x, y, width, height)
|
ctx.fillRect(x, y, width, height)
|
||||||
ctx.restore()
|
ctx.restore()
|
||||||
|
this.clearFillInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,29 @@
|
|||||||
|
import { AbstractRichText } from './AbstractRichText'
|
||||||
import { IEditorOption } from '../../../interface/Editor'
|
import { IEditorOption } from '../../../interface/Editor'
|
||||||
import { Draw } from '../Draw'
|
import { Draw } from '../Draw'
|
||||||
|
|
||||||
export class Strikeout {
|
export class Strikeout extends AbstractRichText {
|
||||||
|
|
||||||
private options: Required<IEditorOption>
|
private options: Required<IEditorOption>
|
||||||
|
|
||||||
constructor(draw: Draw) {
|
constructor(draw: Draw) {
|
||||||
|
super()
|
||||||
this.options = draw.getOptions()
|
this.options = draw.getOptions()
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(ctx: CanvasRenderingContext2D, x: number, y: number, width: number) {
|
public render(ctx: CanvasRenderingContext2D) {
|
||||||
|
if (!this.fillRect.width) return
|
||||||
const { strikeoutColor } = this.options
|
const { strikeoutColor } = this.options
|
||||||
|
const { x, y, width } = this.fillRect
|
||||||
ctx.save()
|
ctx.save()
|
||||||
ctx.strokeStyle = strikeoutColor
|
ctx.strokeStyle = strikeoutColor
|
||||||
|
const adjustY = y + 0.5 // 从1处渲染,避免线宽度等于3
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
ctx.moveTo(x, y)
|
ctx.moveTo(x, adjustY)
|
||||||
ctx.lineTo(x + width, y)
|
ctx.lineTo(x + width, adjustY)
|
||||||
ctx.stroke()
|
ctx.stroke()
|
||||||
ctx.restore()
|
ctx.restore()
|
||||||
|
this.clearFillInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,29 @@
|
|||||||
|
import { AbstractRichText } from './AbstractRichText'
|
||||||
import { IEditorOption } from '../../../interface/Editor'
|
import { IEditorOption } from '../../../interface/Editor'
|
||||||
import { Draw } from '../Draw'
|
import { Draw } from '../Draw'
|
||||||
|
|
||||||
export class Underline {
|
export class Underline extends AbstractRichText {
|
||||||
|
|
||||||
private options: Required<IEditorOption>
|
private options: Required<IEditorOption>
|
||||||
|
|
||||||
constructor(draw: Draw) {
|
constructor(draw: Draw) {
|
||||||
|
super()
|
||||||
this.options = draw.getOptions()
|
this.options = draw.getOptions()
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(ctx: CanvasRenderingContext2D, color: string, x: number, y: number, width: number) {
|
public render(ctx: CanvasRenderingContext2D) {
|
||||||
|
if (!this.fillRect.width) return
|
||||||
const { underlineColor } = this.options
|
const { underlineColor } = this.options
|
||||||
|
const { x, y, width } = this.fillRect
|
||||||
ctx.save()
|
ctx.save()
|
||||||
ctx.strokeStyle = color || underlineColor
|
ctx.strokeStyle = this.fillColor || underlineColor
|
||||||
|
const adjustY = y + 0.5 // 从1处渲染,避免线宽度等于3
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
ctx.moveTo(x, y)
|
ctx.moveTo(x, adjustY)
|
||||||
ctx.lineTo(x + width, y)
|
ctx.lineTo(x + width, adjustY)
|
||||||
ctx.stroke()
|
ctx.stroke()
|
||||||
ctx.restore()
|
ctx.restore()
|
||||||
|
this.clearFillInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user