feat:优化渲染性能
This commit is contained in:
@@ -20,6 +20,7 @@ import { Strikeout } from "./richtext/Strikeout"
|
|||||||
import { Underline } from "./richtext/Underline"
|
import { Underline } from "./richtext/Underline"
|
||||||
import { ElementType } from "../../dataset/enum/Element"
|
import { ElementType } from "../../dataset/enum/Element"
|
||||||
import { ImageParticle } from "./particle/ImageParticle"
|
import { ImageParticle } from "./particle/ImageParticle"
|
||||||
|
import { TextParticle } from "./particle/TextParticle"
|
||||||
|
|
||||||
export class Draw {
|
export class Draw {
|
||||||
|
|
||||||
@@ -40,8 +41,9 @@ export class Draw {
|
|||||||
private highlight: Highlight
|
private highlight: Highlight
|
||||||
private historyManager: HistoryManager
|
private historyManager: HistoryManager
|
||||||
private imageParticle: ImageParticle
|
private imageParticle: ImageParticle
|
||||||
|
private textParticle: TextParticle
|
||||||
|
|
||||||
private rowCount: number
|
private rowList: IRow[]
|
||||||
private painterStyle: IElementStyle | null
|
private painterStyle: IElementStyle | null
|
||||||
private searchMatchList: number[][] | null
|
private searchMatchList: number[][] | null
|
||||||
|
|
||||||
@@ -68,6 +70,7 @@ export class Draw {
|
|||||||
this.strikeout = new Strikeout(ctx, options)
|
this.strikeout = new Strikeout(ctx, options)
|
||||||
this.highlight = new Highlight(ctx, options)
|
this.highlight = new Highlight(ctx, options)
|
||||||
this.imageParticle = new ImageParticle(canvas, ctx, options, this)
|
this.imageParticle = new ImageParticle(canvas, ctx, options, this)
|
||||||
|
this.textParticle = new TextParticle(ctx)
|
||||||
|
|
||||||
const canvasEvent = new CanvasEvent(canvas, this)
|
const canvasEvent = new CanvasEvent(canvas, this)
|
||||||
this.cursor = new Cursor(canvas, this, canvasEvent)
|
this.cursor = new Cursor(canvas, this, canvasEvent)
|
||||||
@@ -75,7 +78,7 @@ export class Draw {
|
|||||||
const globalEvent = new GlobalEvent(canvas, this, canvasEvent)
|
const globalEvent = new GlobalEvent(canvas, this, canvasEvent)
|
||||||
globalEvent.register()
|
globalEvent.register()
|
||||||
|
|
||||||
this.rowCount = 0
|
this.rowList = []
|
||||||
this.painterStyle = null
|
this.painterStyle = null
|
||||||
this.searchMatchList = null
|
this.searchMatchList = null
|
||||||
|
|
||||||
@@ -115,7 +118,7 @@ export class Draw {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getRowCount(): number {
|
public getRowCount(): number {
|
||||||
return this.rowCount
|
return this.rowList.length
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDataURL(): string {
|
public getDataURL(): string {
|
||||||
@@ -155,25 +158,13 @@ export class Draw {
|
|||||||
return `${el.italic ? 'italic ' : ''}${el.bold ? 'bold ' : ''}${el.size || defaultSize}px ${el.font || defaultFont}`
|
return `${el.italic ? 'italic ' : ''}${el.bold ? 'bold ' : ''}${el.size || defaultSize}px ${el.font || defaultFont}`
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(payload?: IDrawOption) {
|
private computeRowList() {
|
||||||
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()
|
|
||||||
// 基础信息
|
|
||||||
const { defaultSize } = this.options
|
const { defaultSize } = this.options
|
||||||
const canvasRect = this.canvas.getBoundingClientRect()
|
const canvasRect = this.canvas.getBoundingClientRect()
|
||||||
// 绘制背景
|
|
||||||
this.background.render(canvasRect)
|
|
||||||
// 绘制页边距
|
|
||||||
const { width } = canvasRect
|
const { width } = canvasRect
|
||||||
const { margins, defaultRowMargin, defaultBasicRowMarginHeight } = this.options
|
const { margins, defaultRowMargin, defaultBasicRowMarginHeight } = this.options
|
||||||
const leftTopPoint: [number, number] = [margins[3], margins[0]]
|
const leftTopPoint: [number, number] = [margins[3], margins[0]]
|
||||||
const rightTopPoint: [number, number] = [width - margins[1], margins[0]]
|
const rightTopPoint: [number, number] = [width - margins[1], margins[0]]
|
||||||
this.margin.render(canvasRect)
|
|
||||||
// 计算行信息
|
|
||||||
const rowList: IRow[] = []
|
const rowList: IRow[] = []
|
||||||
if (this.elementList.length) {
|
if (this.elementList.length) {
|
||||||
rowList.push({
|
rowList.push({
|
||||||
@@ -213,13 +204,17 @@ export class Draw {
|
|||||||
this.ctx.font = this.getFont(element)
|
this.ctx.font = this.getFont(element)
|
||||||
const fontMetrics = this.ctx.measureText(element.value)
|
const fontMetrics = this.ctx.measureText(element.value)
|
||||||
metrics.width = fontMetrics.width
|
metrics.width = fontMetrics.width
|
||||||
metrics.boundingBoxAscent = i === 0 ? defaultSize : fontMetrics.actualBoundingBoxAscent
|
metrics.boundingBoxAscent = element.value === ZERO ? defaultSize : fontMetrics.actualBoundingBoxAscent
|
||||||
metrics.boundingBoxDescent = fontMetrics.actualBoundingBoxDescent
|
metrics.boundingBoxDescent = fontMetrics.actualBoundingBoxDescent
|
||||||
}
|
}
|
||||||
const ascent = metrics.boundingBoxAscent + rowMargin
|
const ascent = metrics.boundingBoxAscent + rowMargin
|
||||||
const descent = metrics.boundingBoxDescent + rowMargin
|
const descent = metrics.boundingBoxDescent + rowMargin
|
||||||
const height = ascent + descent
|
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)) {
|
if (curRow.width + metrics.width > innerWidth || (i !== 0 && element.value === ZERO)) {
|
||||||
rowList.push({
|
rowList.push({
|
||||||
@@ -243,12 +238,39 @@ export class Draw {
|
|||||||
}
|
}
|
||||||
this.ctx.restore()
|
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 x = leftTopPoint[0]
|
||||||
let y = leftTopPoint[1]
|
let y = leftTopPoint[1]
|
||||||
let index = 0
|
let index = 0
|
||||||
for (let i = 0; i < rowList.length; i++) {
|
for (let i = 0; i < this.rowList.length; i++) {
|
||||||
const curRow = rowList[i]
|
const curRow = this.rowList[i]
|
||||||
// 计算行偏移量(行居左、居中、居右)
|
// 计算行偏移量(行居左、居中、居右)
|
||||||
if (curRow.rowFlex && curRow.rowFlex !== RowFlex.LEFT) {
|
if (curRow.rowFlex && curRow.rowFlex !== RowFlex.LEFT) {
|
||||||
const canvasInnerWidth = this.canvas.width - margins[1] - margins[3]
|
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++) {
|
for (let j = 0; j < curRow.elementList.length; j++) {
|
||||||
this.ctx.save()
|
|
||||||
const element = curRow.elementList[j]
|
const element = curRow.elementList[j]
|
||||||
const metrics = element.metrics
|
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
|
const offsetY = element.type === ElementType.IMAGE
|
||||||
? curRow.ascent - element.height!
|
? curRow.ascent - element.height!
|
||||||
: curRow.ascent
|
: curRow.ascent
|
||||||
@@ -301,9 +316,10 @@ export class Draw {
|
|||||||
}
|
}
|
||||||
// 元素绘制
|
// 元素绘制
|
||||||
if (element.type === ElementType.IMAGE) {
|
if (element.type === ElementType.IMAGE) {
|
||||||
|
this.textParticle.complete()
|
||||||
this.imageParticle.render(element, x, y + offsetY)
|
this.imageParticle.render(element, x, y + offsetY)
|
||||||
} else {
|
} else {
|
||||||
this.ctx.fillText(element.value, x, y + offsetY)
|
this.textParticle.record(element, x, y + offsetY)
|
||||||
}
|
}
|
||||||
// 选区绘制
|
// 选区绘制
|
||||||
const { startIndex, endIndex } = this.range.getRange()
|
const { startIndex, endIndex } = this.range.getRange()
|
||||||
@@ -312,11 +328,12 @@ export class Draw {
|
|||||||
}
|
}
|
||||||
index++
|
index++
|
||||||
x += metrics.width
|
x += metrics.width
|
||||||
this.ctx.restore()
|
|
||||||
}
|
}
|
||||||
|
this.textParticle.complete()
|
||||||
x = leftTopPoint[0]
|
x = leftTopPoint[0]
|
||||||
y += curRow.height
|
y += curRow.height
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索匹配绘制
|
// 搜索匹配绘制
|
||||||
if (this.searchMatchList) {
|
if (this.searchMatchList) {
|
||||||
this.search.render()
|
this.search.render()
|
||||||
@@ -336,9 +353,12 @@ export class Draw {
|
|||||||
const height = Math.ceil(leftBottom[1] + (leftBottom[1] - leftTop[1]) + margins[2])
|
const height = Math.ceil(leftBottom[1] + (leftBottom[1] - leftTop[1]) + margins[2])
|
||||||
this.canvas.height = height
|
this.canvas.height = height
|
||||||
this.canvas.style.height = `${height}px`
|
this.canvas.style.height = `${height}px`
|
||||||
this.render({ curIndex, isSubmitHistory: false })
|
this.render({
|
||||||
|
curIndex,
|
||||||
|
isSubmitHistory: false,
|
||||||
|
isComputeRowList: false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
this.rowCount = rowList.length
|
|
||||||
// 历史记录用于undo、redo
|
// 历史记录用于undo、redo
|
||||||
if (isSubmitHistory) {
|
if (isSubmitHistory) {
|
||||||
const self = this
|
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({
|
this.draw.render({
|
||||||
isSubmitHistory: false,
|
isSubmitHistory: false,
|
||||||
isSetCursor: false
|
isSetCursor: false,
|
||||||
|
isComputeRowList: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +96,12 @@ export class CanvasEvent {
|
|||||||
const isDirectHitImage = isDirectHit && isImage
|
const isDirectHitImage = isDirectHit && isImage
|
||||||
if (~index) {
|
if (~index) {
|
||||||
this.range.setRange(index, 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()
|
this.imageParticle.clearResizer()
|
||||||
@@ -151,13 +157,21 @@ export class CanvasEvent {
|
|||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
const curIndex = index - 1
|
const curIndex = index - 1
|
||||||
this.range.setRange(curIndex, curIndex)
|
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) {
|
} else if (evt.key === KeyMap.Right) {
|
||||||
if (index < position.length - 1) {
|
if (index < position.length - 1) {
|
||||||
const curIndex = index + 1
|
const curIndex = index + 1
|
||||||
this.range.setRange(curIndex, curIndex)
|
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) {
|
} else if (evt.key === KeyMap.Up || evt.key === KeyMap.Down) {
|
||||||
const { rowNo, index, coordinate: { leftTop, rightTop } } = cursorPosition
|
const { rowNo, index, coordinate: { leftTop, rightTop } } = cursorPosition
|
||||||
@@ -194,7 +208,11 @@ export class CanvasEvent {
|
|||||||
}
|
}
|
||||||
const curIndex = maxIndex
|
const curIndex = maxIndex
|
||||||
this.range.setRange(curIndex, curIndex)
|
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) {
|
} else if (evt.ctrlKey && evt.key === KeyMap.Z) {
|
||||||
this.historyManager.undo()
|
this.historyManager.undo()
|
||||||
@@ -216,7 +234,11 @@ export class CanvasEvent {
|
|||||||
}
|
}
|
||||||
} else if (evt.ctrlKey && evt.key === KeyMap.A) {
|
} else if (evt.ctrlKey && evt.key === KeyMap.A) {
|
||||||
this.range.setRange(0, position.length - 1)
|
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 => ({
|
const inputData: IElement[] = data.split('').map(value => ({
|
||||||
value
|
value
|
||||||
}))
|
}))
|
||||||
|
let start = 0
|
||||||
if (isCollspace) {
|
if (isCollspace) {
|
||||||
elementList.splice(index + 1, 0, ...inputData)
|
start = index + 1
|
||||||
} else {
|
} 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
|
const curIndex = (isCollspace ? index : startIndex) + inputData.length
|
||||||
this.range.setRange(curIndex, curIndex)
|
this.range.setRange(curIndex, curIndex)
|
||||||
|
|||||||
+5
-2
@@ -47,12 +47,15 @@ export default class Editor {
|
|||||||
value: ZERO
|
value: ZERO
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
elementList.forEach(el => {
|
for (let i = 0; i < elementList.length; i++) {
|
||||||
|
const el = elementList[i]
|
||||||
if (el.value === '\n') {
|
if (el.value === '\n') {
|
||||||
el.value = ZERO
|
el.value = ZERO
|
||||||
}
|
}
|
||||||
|
if (el.type === ElementType.IMAGE) {
|
||||||
el.id = getUUID()
|
el.id = getUUID()
|
||||||
})
|
}
|
||||||
|
}
|
||||||
// 监听
|
// 监听
|
||||||
this.listener = new Listener()
|
this.listener = new Listener()
|
||||||
// 启动
|
// 启动
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ export interface IDrawOption {
|
|||||||
curIndex?: number;
|
curIndex?: number;
|
||||||
isSetCursor?: boolean;
|
isSetCursor?: boolean;
|
||||||
isSubmitHistory?: boolean;
|
isSubmitHistory?: boolean;
|
||||||
|
isComputeRowList?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IDrawImagePayload {
|
export interface IDrawImagePayload {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import { RowFlex } from "../dataset/enum/Row"
|
|||||||
import { IElement, IElementMetrics } from "./Element"
|
import { IElement, IElementMetrics } from "./Element"
|
||||||
|
|
||||||
export type IRowElement = IElement & {
|
export type IRowElement = IElement & {
|
||||||
metrics: IElementMetrics
|
metrics: IElementMetrics;
|
||||||
|
style: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRow {
|
export interface IRow {
|
||||||
|
|||||||
Reference in New Issue
Block a user