feat:optimize rich text render performance

pr675
黄云飞 4 years ago
parent 7573eeece1
commit a4abaca5a7

@ -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()
} }
} }
Loading…
Cancel
Save