feat:优化渲染性能

pr675
黄云飞 4 years ago
parent 0f8f228137
commit c7b8832789

@ -20,6 +20,7 @@ import { Strikeout } from "./richtext/Strikeout"
import { Underline } from "./richtext/Underline"
import { ElementType } from "../../dataset/enum/Element"
import { ImageParticle } from "./particle/ImageParticle"
import { TextParticle } from "./particle/TextParticle"
export class Draw {
@ -40,8 +41,9 @@ export class Draw {
private highlight: Highlight
private historyManager: HistoryManager
private imageParticle: ImageParticle
private textParticle: TextParticle
private rowCount: number
private rowList: IRow[]
private painterStyle: IElementStyle | null
private searchMatchList: number[][] | null
@ -68,6 +70,7 @@ export class Draw {
this.strikeout = new Strikeout(ctx, options)
this.highlight = new Highlight(ctx, options)
this.imageParticle = new ImageParticle(canvas, ctx, options, this)
this.textParticle = new TextParticle(ctx)
const canvasEvent = new CanvasEvent(canvas, this)
this.cursor = new Cursor(canvas, this, canvasEvent)
@ -75,7 +78,7 @@ export class Draw {
const globalEvent = new GlobalEvent(canvas, this, canvasEvent)
globalEvent.register()
this.rowCount = 0
this.rowList = []
this.painterStyle = null
this.searchMatchList = null
@ -115,7 +118,7 @@ export class Draw {
}
public getRowCount(): number {
return this.rowCount
return this.rowList.length
}
public getDataURL(): string {
@ -155,25 +158,13 @@ export class Draw {
return `${el.italic ? 'italic ' : ''}${el.bold ? 'bold ' : ''}${el.size || defaultSize}px ${el.font || defaultFont}`
}
public render(payload?: IDrawOption) {
let { curIndex, isSubmitHistory = true, isSetCursor = true } = payload || {}
// 清除光标等副作用
this.cursor.recoveryCursor()
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
this.position.setPositionList([])
const positionList = this.position.getPositionList()
// 基础信息
private computeRowList() {
const { defaultSize } = this.options
const canvasRect = this.canvas.getBoundingClientRect()
// 绘制背景
this.background.render(canvasRect)
// 绘制页边距
const { width } = canvasRect
const { margins, defaultRowMargin, defaultBasicRowMarginHeight } = this.options
const leftTopPoint: [number, number] = [margins[3], margins[0]]
const rightTopPoint: [number, number] = [width - margins[1], margins[0]]
this.margin.render(canvasRect)
// 计算行信息
const rowList: IRow[] = []
if (this.elementList.length) {
rowList.push({
@ -213,13 +204,17 @@ export class Draw {
this.ctx.font = this.getFont(element)
const fontMetrics = this.ctx.measureText(element.value)
metrics.width = fontMetrics.width
metrics.boundingBoxAscent = i === 0 ? defaultSize : fontMetrics.actualBoundingBoxAscent
metrics.boundingBoxAscent = element.value === ZERO ? defaultSize : fontMetrics.actualBoundingBoxAscent
metrics.boundingBoxDescent = fontMetrics.actualBoundingBoxDescent
}
const ascent = metrics.boundingBoxAscent + rowMargin
const descent = metrics.boundingBoxDescent + rowMargin
const height = ascent + descent
const rowElement: IRowElement = { ...element, metrics }
const rowElement: IRowElement = {
...element,
metrics,
style: this.ctx.font
}
// 超过限定宽度
if (curRow.width + metrics.width > innerWidth || (i !== 0 && element.value === ZERO)) {
rowList.push({
@ -243,12 +238,39 @@ export class Draw {
}
this.ctx.restore()
}
this.rowList = rowList
}
public render(payload?: IDrawOption) {
let {
curIndex,
isSubmitHistory = true,
isSetCursor = true,
isComputeRowList = true
} = payload || {}
// 清除光标等副作用
this.cursor.recoveryCursor()
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
this.position.setPositionList([])
const positionList = this.position.getPositionList()
// 基础信息
const canvasRect = this.canvas.getBoundingClientRect()
// 绘制背景
this.background.render(canvasRect)
// 绘制页边距
const { margins } = this.options
const leftTopPoint: [number, number] = [margins[3], margins[0]]
this.margin.render(canvasRect)
// 计算行信息
if (isComputeRowList) {
this.computeRowList()
}
// 渲染元素
let x = leftTopPoint[0]
let y = leftTopPoint[1]
let index = 0
for (let i = 0; i < rowList.length; i++) {
const curRow = rowList[i]
for (let i = 0; i < this.rowList.length; i++) {
const curRow = this.rowList[i]
// 计算行偏移量(行居左、居中、居右)
if (curRow.rowFlex && curRow.rowFlex !== RowFlex.LEFT) {
const canvasInnerWidth = this.canvas.width - margins[1] - margins[3]
@ -259,15 +281,8 @@ export class Draw {
}
}
for (let j = 0; j < curRow.elementList.length; j++) {
this.ctx.save()
const element = curRow.elementList[j]
const metrics = element.metrics
if (!element.type || element.type === ElementType.TEXT) {
this.ctx.font = this.getFont(element)
if (element.color) {
this.ctx.fillStyle = element.color
}
}
const offsetY = element.type === ElementType.IMAGE
? curRow.ascent - element.height!
: curRow.ascent
@ -301,9 +316,10 @@ export class Draw {
}
// 元素绘制
if (element.type === ElementType.IMAGE) {
this.textParticle.complete()
this.imageParticle.render(element, x, y + offsetY)
} else {
this.ctx.fillText(element.value, x, y + offsetY)
this.textParticle.record(element, x, y + offsetY)
}
// 选区绘制
const { startIndex, endIndex } = this.range.getRange()
@ -312,11 +328,12 @@ export class Draw {
}
index++
x += metrics.width
this.ctx.restore()
}
this.textParticle.complete()
x = leftTopPoint[0]
y += curRow.height
}
// 搜索匹配绘制
if (this.searchMatchList) {
this.search.render()
@ -336,9 +353,12 @@ export class Draw {
const height = Math.ceil(leftBottom[1] + (leftBottom[1] - leftTop[1]) + margins[2])
this.canvas.height = height
this.canvas.style.height = `${height}px`
this.render({ curIndex, isSubmitHistory: false })
this.render({
curIndex,
isSubmitHistory: false,
isComputeRowList: false
})
}
this.rowCount = rowList.length
// 历史记录用于undo、redo
if (isSubmitHistory) {
const self = this

@ -0,0 +1,59 @@
import { IRowElement } from "../../../interface/Row"
export class TextParticle {
private ctx: CanvasRenderingContext2D
private curX: number
private curY: number
private text: string
private curStyle: string
private curColor?: string
constructor(ctx: CanvasRenderingContext2D) {
this.ctx = ctx
this.curX = -1
this.curY = -1
this.text = ''
this.curStyle = ''
}
public complete() {
this.render()
this.text = ''
}
public record(element: IRowElement, x: number, y: number) {
// 主动完成的重设起始点
if (!this.text) {
this.setCurXY(x, y)
}
// 样式发生改变
if (
(this.curStyle && element.style !== this.curStyle) ||
(element.color !== this.curColor)
) {
this.complete()
this.setCurXY(x, y)
}
this.text += element.value
this.curStyle = element.style
this.curColor = element.color
}
private setCurXY(x: number, y: number) {
this.curX = x
this.curY = y
}
private render() {
if (!this.text || !~this.curX || !~this.curX) return
this.ctx.save()
this.ctx.font = this.curStyle
if (this.curColor) {
this.ctx.fillStyle = this.curColor
}
this.ctx.fillText(this.text, this.curX, this.curY)
this.ctx.restore()
}
}

@ -82,7 +82,8 @@ export class CanvasEvent {
// 绘制
this.draw.render({
isSubmitHistory: false,
isSetCursor: false
isSetCursor: false,
isComputeRowList: false
})
}
@ -95,7 +96,12 @@ export class CanvasEvent {
const isDirectHitImage = isDirectHit && isImage
if (~index) {
this.range.setRange(index, index)
this.draw.render({ curIndex: index, isSubmitHistory: false, isSetCursor: !isDirectHitImage })
this.draw.render({
curIndex: index,
isSubmitHistory: false,
isSetCursor: !isDirectHitImage,
isComputeRowList: false
})
}
// 图片尺寸拖拽组件
this.imageParticle.clearResizer()
@ -151,13 +157,21 @@ export class CanvasEvent {
if (index > 0) {
const curIndex = index - 1
this.range.setRange(curIndex, curIndex)
this.draw.render({ curIndex, isSubmitHistory: false })
this.draw.render({
curIndex,
isSubmitHistory: false,
isComputeRowList: false
})
}
} else if (evt.key === KeyMap.Right) {
if (index < position.length - 1) {
const curIndex = index + 1
this.range.setRange(curIndex, curIndex)
this.draw.render({ curIndex, isSubmitHistory: false })
this.draw.render({
curIndex,
isSubmitHistory: false,
isComputeRowList: false
})
}
} else if (evt.key === KeyMap.Up || evt.key === KeyMap.Down) {
const { rowNo, index, coordinate: { leftTop, rightTop } } = cursorPosition
@ -194,7 +208,11 @@ export class CanvasEvent {
}
const curIndex = maxIndex
this.range.setRange(curIndex, curIndex)
this.draw.render({ curIndex, isSubmitHistory: false })
this.draw.render({
curIndex,
isSubmitHistory: false,
isComputeRowList: false
})
}
} else if (evt.ctrlKey && evt.key === KeyMap.Z) {
this.historyManager.undo()
@ -216,7 +234,11 @@ export class CanvasEvent {
}
} else if (evt.ctrlKey && evt.key === KeyMap.A) {
this.range.setRange(0, position.length - 1)
this.draw.render({ isSubmitHistory: false, isSetCursor: false })
this.draw.render({
isSubmitHistory: false,
isSetCursor: false,
isComputeRowList: false
})
}
}
@ -233,10 +255,16 @@ export class CanvasEvent {
const inputData: IElement[] = data.split('').map(value => ({
value
}))
let start = 0
if (isCollspace) {
elementList.splice(index + 1, 0, ...inputData)
start = index + 1
} else {
elementList.splice(startIndex + 1, endIndex - startIndex, ...inputData)
start = startIndex + 1
elementList.splice(startIndex + 1, endIndex - startIndex)
}
// 禁止直接使用解构存在性能问题
for (let i = 0; i < inputData.length; i++) {
elementList.splice(start + i, 0, inputData[i])
}
const curIndex = (isCollspace ? index : startIndex) + inputData.length
this.range.setRange(curIndex, curIndex)

@ -47,12 +47,15 @@ export default class Editor {
value: ZERO
})
}
elementList.forEach(el => {
for (let i = 0; i < elementList.length; i++) {
const el = elementList[i]
if (el.value === '\n') {
el.value = ZERO
}
el.id = getUUID()
})
if (el.type === ElementType.IMAGE) {
el.id = getUUID()
}
}
// 监听
this.listener = new Listener()
// 启动

@ -2,6 +2,7 @@ export interface IDrawOption {
curIndex?: number;
isSetCursor?: boolean;
isSubmitHistory?: boolean;
isComputeRowList?: boolean;
}
export interface IDrawImagePayload {

@ -2,7 +2,8 @@ import { RowFlex } from "../dataset/enum/Row"
import { IElement, IElementMetrics } from "./Element"
export type IRowElement = IElement & {
metrics: IElementMetrics
metrics: IElementMetrics;
style: string;
}
export interface IRow {

Loading…
Cancel
Save