Merge pull request #135 from Hufe921/feature/performance

Feature/performance
pr675
Hufe 3 years ago committed by GitHub
commit 33475a252b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -10,16 +10,16 @@ describe('菜单-打印', () => {
it('打印', () => {
cy.getEditor().then((editor: Editor) => {
const imageList2 = editor.command.getImage()
cy.getEditor().then(async (editor: Editor) => {
const imageList2 = await editor.command.getImage()
expect(imageList2.length).to.eq(2)
editor.command.executeSelectAll()
editor.command.executeBackspace()
cy.wait(200).then(() => {
const imageList1 = editor.command.getImage()
cy.wait(200).then(async () => {
const imageList1 = await editor.command.getImage()
expect(imageList1.length).to.eq(1)
})
})

@ -381,7 +381,7 @@ export class Command {
return Command.changeImageDisplay(element, display)
}
public getImage(): string[] {
public getImage(): Promise<string[]> {
return Command.getImage()
}

@ -118,7 +118,7 @@ export class CommandAdapt {
const isCollapsed = startIndex === endIndex
this.draw.render({
curIndex: isCollapsed ? startIndex : undefined,
isComputeRowList: false,
isCompute: false,
isSubmitHistory: false,
isSetCursor: isCollapsed
})
@ -327,7 +327,10 @@ export class CommandAdapt {
selection.forEach(el => {
el.color = payload
})
this.draw.render({ isSetCursor: false })
this.draw.render({
isSetCursor: false,
isCompute: false
})
}
public highlight(payload: string) {
@ -338,7 +341,10 @@ export class CommandAdapt {
selection.forEach(el => {
el.highlight = payload
})
this.draw.render({ isSetCursor: false })
this.draw.render({
isSetCursor: false,
isCompute: false
})
}
public rowFlex(payload: RowFlex) {
@ -1092,7 +1098,7 @@ export class CommandAdapt {
const { endIndex } = this.range.getRange()
this.draw.render({
curIndex: endIndex,
isComputeRowList: false
isCompute: false
})
}
@ -1112,7 +1118,7 @@ export class CommandAdapt {
const { endIndex } = this.range.getRange()
this.draw.render({
curIndex: endIndex,
isComputeRowList: false
isCompute: false
})
}
@ -1173,7 +1179,8 @@ export class CommandAdapt {
options.watermark.font = payload.font || font
this.draw.render({
isSetCursor: false,
isSubmitHistory: false
isSubmitHistory: false,
isCompute: false
})
}
@ -1185,7 +1192,8 @@ export class CommandAdapt {
options.watermark = { ...defaultWatermarkOption }
this.draw.render({
isSetCursor: false,
isSubmitHistory: false
isSubmitHistory: false,
isCompute: false
})
}
}
@ -1229,7 +1237,9 @@ export class CommandAdapt {
if (index === null) return
this.draw.render({
isSetCursor: false,
isSubmitHistory: false
isSubmitHistory: false,
isCompute: false,
isLazy: false
})
}
@ -1238,7 +1248,9 @@ export class CommandAdapt {
if (index === null) return
this.draw.render({
isSetCursor: false,
isSubmitHistory: false
isSubmitHistory: false,
isCompute: false,
isLazy: false
})
}
@ -1352,12 +1364,13 @@ export class CommandAdapt {
})
}
public print() {
public async print() {
const { width, height, scale } = this.options
if (scale !== 1) {
this.draw.setPageScale(1)
}
printImageBase64(this.draw.getDataURL(), width, height)
const base64List = await this.draw.getDataURL()
printImageBase64(base64List, width, height)
if (scale !== 1) {
this.draw.setPageScale(scale)
}
@ -1393,7 +1406,7 @@ export class CommandAdapt {
})
}
public getImage(): string[] {
public getImage(): Promise<string[]> {
return this.draw.getDataURL()
}

@ -1,11 +1,11 @@
import { version } from '../../../../package.json'
import { ZERO } from '../../dataset/constant/Common'
import { RowFlex } from '../../dataset/enum/Row'
import { IDrawOption, IDrawRowPayload, IDrawRowResult, IPainterOptions } from '../../interface/Draw'
import { IDrawOption, IDrawRowPayload, IPainterOptions } from '../../interface/Draw'
import { IEditorOption, IEditorResult } from '../../interface/Editor'
import { IElement, IElementMetrics, IElementPosition, IElementFillRect, IElementStyle } from '../../interface/Element'
import { IRow, IRowElement } from '../../interface/Row'
import { deepClone, getUUID } from '../../utils'
import { deepClone, getUUID, nextTick } from '../../utils'
import { Cursor } from '../cursor/Cursor'
import { CanvasEvent } from '../event/CanvasEvent'
import { GlobalEvent } from '../event/GlobalEvent'
@ -49,6 +49,7 @@ import { IMargin } from '../../interface/Margin'
import { BlockParticle } from './particle/block/BlockParticle'
import { EDITOR_COMPONENT, EDITOR_PREFIX } from '../../dataset/constant/Editor'
import { I18n } from '../i18n/I18n'
import { ImageObserver } from '../observer/ImageObserver'
export class Draw {
@ -96,12 +97,15 @@ export class Draw {
private workerManager: WorkerManager
private scrollObserver: ScrollObserver
private selectionObserver: SelectionObserver
private imageObserver: ImageObserver
private rowList: IRow[]
private pageRowList: IRow[][]
private painterStyle: IElementStyle | null
private painterOptions: IPainterOptions | null
private visiblePageNoList: number[]
private intersectionPageNo: number
private lazyRenderIntersectionObserver: IntersectionObserver | null
constructor(
rootContainer: HTMLElement,
@ -153,6 +157,7 @@ export class Draw {
this.scrollObserver = new ScrollObserver(this)
this.selectionObserver = new SelectionObserver()
this.imageObserver = new ImageObserver()
this.canvasEvent = new CanvasEvent(this)
this.cursor = new Cursor(this, this.canvasEvent)
@ -163,10 +168,12 @@ export class Draw {
this.workerManager = new WorkerManager(this)
this.rowList = []
this.pageRowList = []
this.painterStyle = null
this.painterOptions = null
this.visiblePageNoList = []
this.intersectionPageNo = 0
this.lazyRenderIntersectionObserver = null
this.render({ isSetCursor: false })
}
@ -290,6 +297,14 @@ export class Draw {
return this.pageList
}
public getRowList(): IRow[] {
return this.rowList
}
public getPageRowList(): IRow[][] {
return this.pageRowList
}
public getCtx(): CanvasRenderingContext2D {
return this.ctxList[this.pageNo]
}
@ -405,6 +420,10 @@ export class Draw {
return this.workerManager
}
public getImageObserver(): ImageObserver {
return this.imageObserver
}
public getI18n(): I18n {
return this.i18n
}
@ -413,7 +432,14 @@ export class Draw {
return this.rowList.length
}
public getDataURL(): string[] {
public async getDataURL(): Promise<string[]> {
this.render({
isLazy: false,
isCompute: false,
isSetCursor: false,
isSubmitHistory: false
})
await this.imageObserver.allSettled()
return this.pageList.map(c => c.toDataURL())
}
@ -599,7 +625,6 @@ export class Draw {
private _computeRowList(innerWidth: number, elementList: IElement[]) {
const { defaultSize, defaultRowMargin, scale, tdPadding, defaultTabWidth } = this.options
const defaultBasicRowMarginHeight = this.getDefaultBasicRowMarginHeight()
const tdGap = tdPadding * 2
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
const rowList: IRow[] = []
@ -609,6 +634,7 @@ export class Draw {
height: 0,
ascent: 0,
elementList: [],
startIndex: 0,
rowFlex: elementList?.[1]?.rowFlex
})
}
@ -642,6 +668,7 @@ export class Draw {
}
metrics.boundingBoxAscent = 0
} else if (element.type === ElementType.TABLE) {
const tdGap = tdPadding * 2
// 计算表格行列
this.tableParticle.computeRowColInfo(element)
// 计算表格内元素信息
@ -820,6 +847,7 @@ export class Draw {
rowList.push({
width: metrics.width,
height,
startIndex: i,
elementList: [rowElement],
ascent,
rowFlex: elementList[i + 1]?.rowFlex,
@ -841,6 +869,45 @@ export class Draw {
return rowList
}
private _computePageList(): IRow[][] {
const pageRowList: IRow[][] = [[]]
const { pageMode } = this.options
const height = this.getHeight()
const margins = this.getMargins()
const marginHeight = margins[0] + margins[2]
let pageHeight = marginHeight
let pageNo = 0
if (pageMode === PageMode.CONTINUITY) {
pageRowList[0] = this.rowList
// 重置高度
pageHeight += this.rowList.reduce((pre, cur) => pre + cur.height, 0)
const dpr = window.devicePixelRatio
const pageDom = this.pageList[0]
const pageDomHeight = Number(pageDom.style.height.replace('px', ''))
if (pageHeight > pageDomHeight) {
pageDom.style.height = `${pageHeight}px`
pageDom.height = pageHeight * dpr
} else {
const reduceHeight = pageHeight < height ? height : pageHeight
pageDom.style.height = `${reduceHeight}px`
pageDom.height = reduceHeight * dpr
}
} else {
for (let i = 0; i < this.rowList.length; i++) {
const row = this.rowList[i]
if (row.height + pageHeight > height || this.rowList[i - 1]?.isPageBreak) {
pageHeight = marginHeight + row.height
pageRowList.push([row])
pageNo++
} else {
pageHeight += row.height
pageRowList[pageNo].push(row)
}
}
}
return pageRowList
}
private _drawRichText(ctx: CanvasRenderingContext2D) {
this.underline.render(ctx)
this.strikeout.render(ctx)
@ -848,25 +915,13 @@ export class Draw {
this.textParticle.complete()
}
private _drawRow(ctx: CanvasRenderingContext2D, payload: IDrawRowPayload): IDrawRowResult {
const { positionList, rowList, pageNo, startX, startY, startIndex, innerWidth } = payload
private _drawRow(ctx: CanvasRenderingContext2D, payload: IDrawRowPayload) {
const { rowList, pageNo, positionList, startIndex } = payload
const { scale, tdPadding } = this.options
const { isCrossRowCol, tableId } = this.range.getRange()
const tdGap = tdPadding * 2
let x = startX
let y = startY
let index = startIndex
for (let i = 0; i < rowList.length; i++) {
const curRow = rowList[i]
// 计算行偏移量(行居中、居右)
if (curRow.rowFlex === RowFlex.CENTER) {
x += (innerWidth - curRow.width) / 2
} else if (curRow.rowFlex === RowFlex.RIGHT) {
x += innerWidth - curRow.width
}
// 当前td所在位置
const tablePreX = x
const tablePreY = y
// 选区绘制记录
const rangeRecord: IElementFillRect = {
x: 0,
@ -878,28 +933,13 @@ export class Draw {
for (let j = 0; j < curRow.elementList.length; j++) {
const element = curRow.elementList[j]
const metrics = element.metrics
const offsetY =
(element.imgDisplay !== ImageDisplay.INLINE && element.type === ElementType.IMAGE)
|| element.type === ElementType.LATEX
? curRow.ascent - metrics.height
: curRow.ascent
const positionItem: IElementPosition = {
pageNo,
index,
value: element.value,
rowNo: i,
metrics,
// 当前元素位置信息
const {
ascent: offsetY,
lineHeight: curRow.height,
isLastLetter: j === curRow.elementList.length - 1,
coordinate: {
leftTop: [x, y],
leftBottom: [x, y + curRow.height],
rightTop: [x + metrics.width, y],
rightBottom: [x + metrics.width, y + curRow.height]
leftTop: [x, y]
}
}
positionList.push(positionItem)
} = positionList[curRow.startIndex + j]
// 元素绘制
if (element.type === ElementType.IMAGE) {
this._drawRichText(ctx)
@ -1004,30 +1044,22 @@ export class Draw {
}
}
index++
x += metrics.width
// 绘制表格内元素
if (element.type === ElementType.TABLE) {
const tdGap = tdPadding * 2
for (let t = 0; t < element.trList!.length; t++) {
const tr = element.trList![t]
for (let d = 0; d < tr.tdList!.length; d++) {
const td = tr.tdList[d]
td.positionList = []
const drawRowResult = this._drawRow(ctx, {
positionList: td.positionList,
this._drawRow(ctx, {
positionList: td.positionList!,
rowList: td.rowList!,
pageNo,
startIndex: 0,
startX: (td.x! + tdPadding) * scale + tablePreX,
startY: td.y! * scale + tablePreY,
innerWidth: (td.width! - tdGap) * scale
})
x = drawRowResult.x
y = drawRowResult.y
}
}
// 恢复初始x、y
x = tablePreX
y = tablePreY
}
}
// 绘制富文本及文字
@ -1038,12 +1070,10 @@ export class Draw {
this.range.render(ctx, x, y, width, height)
}
if (isCrossRowCol && tableRangeElement && tableRangeElement.id === tableId) {
const { coordinate: { leftTop: [x, y] } } = positionList[curRow.startIndex]
this.tableParticle.drawRange(ctx, tableRangeElement, x, y)
}
x = startX
y += curRow.height
}
return { x, y, index }
}
private _clearPage(pageNo: number) {
@ -1055,31 +1085,22 @@ export class Draw {
private _drawPage(positionList: IElementPosition[], rowList: IRow[], pageNo: number) {
const { pageMode } = this.options
const margins = this.getMargins()
const innerWidth = this.getInnerWidth()
const ctx = this.ctxList[pageNo]
this._clearPage(pageNo)
// 绘制背景
this.background.render(ctx)
// 绘制页边距
const leftTopPoint: [number, number] = [margins[3], margins[0]]
this.margin.render(ctx)
// 渲染元素
let x = leftTopPoint[0]
let y = leftTopPoint[1]
let index = positionList.length
const drawRowResult = this._drawRow(ctx, {
const index = rowList[0].startIndex
this._drawRow(ctx, {
positionList,
rowList,
pageNo,
startIndex: index,
startX: x,
startY: y,
innerWidth
})
x = drawRowResult.x
y = drawRowResult.y
index = drawRowResult.index
// 绘制页眉
this.header.render(ctx)
// 绘制页码
@ -1094,73 +1115,71 @@ export class Draw {
}
}
private _lazyRender() {
const positionList = this.position.getOriginalPositionList()
this.lazyRenderIntersectionObserver?.disconnect()
this.lazyRenderIntersectionObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const index = Number((<HTMLCanvasElement>entry.target).dataset.index)
this._drawPage(positionList, this.pageRowList[index], index)
}
})
})
this.pageList.forEach(el => {
this.lazyRenderIntersectionObserver!.observe(el)
})
}
private _immediateRender() {
const positionList = this.position.getOriginalPositionList()
for (let i = 0; i < this.pageRowList.length; i++) {
this._drawPage(positionList, this.pageRowList[i], i)
}
}
public render(payload?: IDrawOption) {
const { pageMode } = this.options
const {
isSubmitHistory = true,
isSetCursor = true,
isComputeRowList = true
isCompute = true,
isLazy = true
} = payload || {}
let { curIndex } = payload || {}
const height = this.getHeight()
const innerWidth = this.getInnerWidth()
// 计算行信息
if (isComputeRowList) {
// 计算文档信息
if (isCompute) {
// 行信息
this.rowList = this._computeRowList(innerWidth, this.elementList)
// 页面信息
this.pageRowList = this._computePageList()
// 位置信息
this.position.computePositionList()
// 搜索信息
const searchKeyword = this.search.getSearchKeyword()
if (searchKeyword) {
this.search.compute(searchKeyword)
}
}
// 清除光标等副作用
this.imageObserver.clearAll()
this.cursor.recoveryCursor()
this.position.setPositionList([])
// 创建纸张
const positionList = this.position.getOriginalPositionList()
// 按页渲染
const margins = this.getMargins()
const marginHeight = margins[0] + margins[2]
let pageHeight = marginHeight
let pageNo = 0
const pageRowList: IRow[][] = [[]]
if (pageMode === PageMode.CONTINUITY) {
pageRowList[0] = this.rowList
// 重置高度
pageHeight += this.rowList.reduce((pre, cur) => pre + cur.height, 0)
const dpr = window.devicePixelRatio
const pageDom = this.pageList[0]
const pageDomHeight = Number(pageDom.style.height.replace('px', ''))
if (pageHeight > pageDomHeight) {
pageDom.style.height = `${pageHeight}px`
pageDom.height = pageHeight * dpr
} else {
const reduceHeight = pageHeight < height ? height : pageHeight
pageDom.style.height = `${reduceHeight}px`
pageDom.height = reduceHeight * dpr
}
} else {
for (let i = 0; i < this.rowList.length; i++) {
const row = this.rowList[i]
if (row.height + pageHeight > height || this.rowList[i - 1]?.isPageBreak) {
pageHeight = marginHeight + row.height
pageRowList.push([row])
pageNo++
} else {
pageHeight += row.height
pageRowList[pageNo].push(row)
}
}
}
// 绘制元素
for (let i = 0; i < pageRowList.length; i++) {
for (let i = 0; i < this.pageRowList.length; i++) {
if (!this.pageList[i]) {
this._createPage(i)
}
const rowList = pageRowList[i]
this._drawPage(positionList, rowList, i)
}
// 绘制元素
if (isLazy) {
this._lazyRender()
} else {
this._immediateRender()
}
// 移除多余页
setTimeout(() => {
const curPageCount = pageRowList.length
nextTick(() => {
const curPageCount = this.pageRowList.length
const prePageCount = this.pageList.length
if (prePageCount > curPageCount) {
const deleteCount = prePageCount - curPageCount
@ -1203,12 +1222,11 @@ export class Draw {
self.render({ curIndex, isSubmitHistory: false })
})
}
// 信息变动回调
setTimeout(() => {
nextTick(() => {
// 页面尺寸改变
if (this.listener.pageSizeChange) {
this.listener.pageSizeChange(pageRowList.length)
this.listener.pageSizeChange(this.pageRowList.length)
}
// 文档内容改变
if (this.listener.contentChange && isSubmitHistory) {

@ -261,7 +261,11 @@ export class Search {
const searchMatchIndexList = this.getSearchNavigateIndexList()
if (searchMatchIndexList.includes(s)) {
ctx.fillStyle = searchNavigateMatchColor
this.searchNavigateScrollIntoView(position)
// 是否是第一个字符,则移动到可视范围
const preSearchMatch = this.searchMatchList[s - 1]
if (!preSearchMatch || preSearchMatch.groupId !== searchMatch.groupId) {
this.searchNavigateScrollIntoView(position)
}
} else {
ctx.fillStyle = searchMatchColor
}

@ -4,14 +4,20 @@ import { Draw } from '../Draw'
export class ImageParticle {
private draw: Draw
protected options: Required<IEditorOption>
protected imageCache: Map<string, HTMLImageElement>
constructor(draw: Draw) {
this.draw = draw
this.options = draw.getOptions()
this.imageCache = new Map()
}
protected addImageObserver(promise: Promise<unknown>) {
this.draw.getImageObserver().add(promise)
}
public render(ctx: CanvasRenderingContext2D, element: IElement, x: number, y: number) {
const { scale } = this.options
const width = element.width! * scale
@ -20,12 +26,19 @@ export class ImageParticle {
const img = this.imageCache.get(element.id!)!
ctx.drawImage(img, x, y, width, height)
} else {
const img = new Image()
img.src = element.value
img.onload = () => {
ctx.drawImage(img, x, y, width, height)
this.imageCache.set(element.id!, img)
}
const imageLoadPromise = new Promise((resolve, reject) => {
const img = new Image()
img.src = element.value
img.onload = () => {
ctx.drawImage(img, x, y, width, height)
this.imageCache.set(element.id!, img)
resolve(element)
}
img.onerror = (error) => {
reject(error)
}
})
this.addImageObserver(imageLoadPromise)
}
}

@ -21,12 +21,19 @@ export class LaTexParticle extends ImageParticle {
const img = this.imageCache.get(element.value)!
ctx.drawImage(img, x, y, width, height)
} else {
const img = new Image()
img.src = element.laTexSVG!
img.onload = () => {
ctx.drawImage(img, x, y, width, height)
this.imageCache.set(element.value, img)
}
const laTexLoadPromise = new Promise((resolve, reject) => {
const img = new Image()
img.src = element.laTexSVG!
img.onload = () => {
ctx.drawImage(img, x, y, width, height)
this.imageCache.set(element.value, img)
resolve(element)
}
img.onerror = (error) => {
reject(error)
}
})
this.addImageObserver(laTexLoadPromise)
}
}

@ -123,7 +123,7 @@ export class CanvasEvent {
this.draw.render({
isSubmitHistory: false,
isSetCursor: false,
isComputeRowList: false
isCompute: false
})
}

@ -44,7 +44,7 @@ function dblclick(host: CanvasEvent) {
draw.render({
isSubmitHistory: false,
isSetCursor: false,
isComputeRowList: false
isCompute: false
})
}
@ -87,7 +87,7 @@ function threeClick(host: CanvasEvent) {
draw.render({
isSubmitHistory: false,
isSetCursor: false,
isComputeRowList: false
isCompute: false
})
}

@ -116,7 +116,7 @@ export function keydown(evt: KeyboardEvent, host: CanvasEvent) {
curIndex: isCollapsed ? anchorStartIndex : undefined,
isSetCursor: isCollapsed,
isSubmitHistory: false,
isComputeRowList: false
isCompute: false
})
evt.preventDefault()
}
@ -150,7 +150,7 @@ export function keydown(evt: KeyboardEvent, host: CanvasEvent) {
curIndex: isCollapsed ? anchorStartIndex : undefined,
isSetCursor: isCollapsed,
isSubmitHistory: false,
isComputeRowList: false
isCompute: false
})
evt.preventDefault()
}
@ -229,7 +229,7 @@ export function keydown(evt: KeyboardEvent, host: CanvasEvent) {
curIndex: isCollapsed ? anchorStartIndex : undefined,
isSetCursor: isCollapsed,
isSubmitHistory: false,
isComputeRowList: false
isCompute: false
})
}
} else if (isMod(evt) && evt.key === KeyMap.Z) {

@ -79,7 +79,7 @@ export function mousedown(evt: MouseEvent, host: CanvasEvent) {
curIndex,
isSubmitHistory: isSetCheckbox,
isSetCursor: !isDirectHitImage && !isDirectHitCheckbox,
isComputeRowList: false
isCompute: false
})
}
// 预览工具组件

@ -73,6 +73,6 @@ export function mousemove(evt: MouseEvent, host: CanvasEvent) {
draw.render({
isSubmitHistory: false,
isSetCursor: false,
isComputeRowList: false
isCompute: false
})
}

@ -0,0 +1,21 @@
export class ImageObserver {
private promiseList: Promise<unknown>[]
constructor() {
this.promiseList = []
}
public add(payload: Promise<unknown>) {
this.promiseList.push(payload)
}
public clearAll() {
this.promiseList = []
}
public allSettled() {
return Promise.allSettled(this.promiseList)
}
}

@ -1,6 +1,7 @@
import { ElementType } from '../..'
import { ElementType, RowFlex } from '../..'
import { ZERO } from '../../dataset/constant/Common'
import { ControlComponent } from '../../dataset/enum/Control'
import { ControlComponent, ImageDisplay } from '../../dataset/enum/Control'
import { IComputePageRowPositionPayload, IComputePageRowPositionResult } from '../../interface/Position'
import { IEditorOption } from '../../interface/Editor'
import { IElementPosition } from '../../interface/Element'
import { ICurrentPosition, IGetPositionByXYPayload, IPositionContext } from '../../interface/Position'
@ -45,6 +46,106 @@ export class Position {
this.positionList = payload
}
private computePageRowPosition(payload: IComputePageRowPositionPayload): IComputePageRowPositionResult {
const { positionList, rowList, pageNo, startX, startY, startIndex, innerWidth } = payload
const { scale, tdPadding } = this.options
let x = startX
let y = startY
let index = startIndex
for (let i = 0; i < rowList.length; i++) {
const curRow = rowList[i]
// 计算行偏移量(行居中、居右)
if (curRow.rowFlex === RowFlex.CENTER) {
x += (innerWidth - curRow.width) / 2
} else if (curRow.rowFlex === RowFlex.RIGHT) {
x += innerWidth - curRow.width
}
// 当前td所在位置
const tablePreX = x
const tablePreY = y
for (let j = 0; j < curRow.elementList.length; j++) {
const element = curRow.elementList[j]
const metrics = element.metrics
const offsetY =
(element.imgDisplay !== ImageDisplay.INLINE && element.type === ElementType.IMAGE)
|| element.type === ElementType.LATEX
? curRow.ascent - metrics.height
: curRow.ascent
const positionItem: IElementPosition = {
pageNo,
index,
value: element.value,
rowNo: i,
metrics,
ascent: offsetY,
lineHeight: curRow.height,
isLastLetter: j === curRow.elementList.length - 1,
coordinate: {
leftTop: [x, y],
leftBottom: [x, y + curRow.height],
rightTop: [x + metrics.width, y],
rightBottom: [x + metrics.width, y + curRow.height]
}
}
positionList.push(positionItem)
index++
x += metrics.width
// 绘制表格内元素
if (element.type === ElementType.TABLE) {
const tdGap = tdPadding * 2
for (let t = 0; t < element.trList!.length; t++) {
const tr = element.trList![t]
for (let d = 0; d < tr.tdList!.length; d++) {
const td = tr.tdList[d]
td.positionList = []
const drawRowResult = this.computePageRowPosition({
positionList: td.positionList,
rowList: td.rowList!,
pageNo,
startIndex: 0,
startX: (td.x! + tdPadding) * scale + tablePreX,
startY: td.y! * scale + tablePreY,
innerWidth: (td.width! - tdGap) * scale
})
x = drawRowResult.x
y = drawRowResult.y
}
}
// 恢复初始x、y
x = tablePreX
y = tablePreY
}
}
x = startX
y += curRow.height
}
return { x, y, index }
}
public computePositionList() {
// 置空原位置信息
this.positionList = []
// 按每页行计算
const innerWidth = this.draw.getInnerWidth()
const pageRowList = this.draw.getPageRowList()
const margins = this.draw.getMargins()
const startX = margins[3]
const startY = margins[0]
for (let i = 0; i < pageRowList.length; i++) {
const rowList = pageRowList[i]
const startIndex = rowList[0].startIndex
this.computePageRowPosition({
positionList: this.positionList,
rowList,
pageNo: i,
startIndex,
startX,
startY,
innerWidth
})
}
}
public setCursorPosition(position: IElementPosition | null) {
this.cursorPosition = position
}

@ -5,7 +5,8 @@ export interface IDrawOption {
curIndex?: number;
isSetCursor?: boolean;
isSubmitHistory?: boolean;
isComputeRowList?: boolean;
isCompute?: boolean;
isLazy?: boolean;
}
export interface IDrawImagePayload {
@ -19,17 +20,9 @@ export interface IDrawRowPayload {
rowList: IRow[];
pageNo: number;
startIndex: number;
startX: number;
startY: number;
innerWidth: number;
}
export interface IDrawRowResult {
x: number;
y: number;
index: number;
}
export interface IPainterOptions {
isDblclick: boolean;
}

@ -1,5 +1,6 @@
import { IElement } from '..'
import { IElementPosition } from './Element'
import { IRow } from './Row'
import { ITd } from './table/Td'
export interface ICurrentPosition {
@ -38,3 +39,19 @@ export interface IPositionContext {
trId?: string;
tableId?: string;
}
export interface IComputePageRowPositionPayload {
positionList: IElementPosition[];
rowList: IRow[];
pageNo: number;
startIndex: number;
startX: number;
startY: number;
innerWidth: number;
}
export interface IComputePageRowPositionResult {
x: number;
y: number;
index: number;
}

@ -11,6 +11,7 @@ export interface IRow {
height: number;
ascent: number;
rowFlex?: RowFlex;
startIndex: number;
isPageBreak?: boolean;
elementList: IRowElement[];
}

@ -112,3 +112,9 @@ export function mergeObject<T>(source: T, target: T): T {
}
return target
}
export function nextTick(fn: Function) {
setTimeout(() => {
fn()
}, 0)
}
Loading…
Cancel
Save