improve:position compute separate from draw row

pr675
Hufe921 3 years ago
parent fcb96a6f56
commit 8910c7cf0a

@ -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
})
@ -1092,7 +1092,7 @@ export class CommandAdapt {
const { endIndex } = this.range.getRange()
this.draw.render({
curIndex: endIndex,
isComputeRowList: false
isCompute: false
})
}
@ -1112,7 +1112,7 @@ export class CommandAdapt {
const { endIndex } = this.range.getRange()
this.draw.render({
curIndex: endIndex,
isComputeRowList: false
isCompute: false
})
}

@ -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'
@ -98,6 +98,7 @@ export class Draw {
private selectionObserver: SelectionObserver
private rowList: IRow[]
private pageRowList: IRow[][]
private painterStyle: IElementStyle | null
private painterOptions: IPainterOptions | null
private visiblePageNoList: number[]
@ -163,6 +164,7 @@ export class Draw {
this.workerManager = new WorkerManager(this)
this.rowList = []
this.pageRowList = []
this.painterStyle = null
this.painterOptions = null
this.visiblePageNoList = []
@ -290,6 +292,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]
}
@ -596,7 +606,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[] = []
@ -606,6 +615,7 @@ export class Draw {
height: 0,
ascent: 0,
elementList: [],
startIndex: 0,
rowFlex: elementList?.[1]?.rowFlex
})
}
@ -639,6 +649,7 @@ export class Draw {
}
metrics.boundingBoxAscent = 0
} else if (element.type === ElementType.TABLE) {
const tdGap = tdPadding * 2
// 计算表格行列
this.tableParticle.computeRowColInfo(element)
// 计算表格内元素信息
@ -817,6 +828,7 @@ export class Draw {
rowList.push({
width: metrics.width,
height,
startIndex: i,
elementList: [rowElement],
ascent,
rowFlex: elementList[i + 1]?.rowFlex,
@ -838,6 +850,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)
@ -845,25 +896,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,
@ -875,28 +914,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)
@ -1001,30 +1025,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
}
}
// 绘制富文本及文字
@ -1035,12 +1051,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) {
@ -1052,31 +1066,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)
// 绘制页码
@ -1092,72 +1097,54 @@ export class Draw {
}
public render(payload?: IDrawOption) {
const { pageMode } = this.options
const {
isSubmitHistory = true,
isSetCursor = true,
isComputeRowList = true
isCompute = 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.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++) {
const positionList = this.position.getOriginalPositionList()
const { visiblePageNoList } = this.scrollObserver.getPageVisibleInfo()
const minPageNo = visiblePageNoList[0]
const maxPageNo = visiblePageNoList[visiblePageNoList.length - 1]
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 (i >= minPageNo && i <= maxPageNo) {
this._drawPage(positionList, this.pageRowList[i], i)
}
}
// 渲染后续不可见页
nextTick(() => {
for (let i = 0; i < this.pageRowList.length; i++) {
if (i < minPageNo || i > maxPageNo) {
this._drawPage(positionList, this.pageRowList[i], i)
}
}
})
// 移除多余页
setTimeout(() => {
const curPageCount = pageRowList.length
nextTick(() => {
const curPageCount = this.pageRowList.length
const prePageCount = this.pageList.length
if (prePageCount > curPageCount) {
const deleteCount = prePageCount - curPageCount
@ -1205,7 +1192,7 @@ export class Draw {
setTimeout(() => {
// 页面尺寸改变
if (this.listener.pageSizeChange) {
this.listener.pageSizeChange(pageRowList.length)
this.listener.pageSizeChange(this.pageRowList.length)
}
// 文档内容改变
if (this.listener.contentChange && isSubmitHistory) {

@ -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
})
}

@ -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,7 @@ export interface IDrawOption {
curIndex?: number;
isSetCursor?: boolean;
isSubmitHistory?: boolean;
isComputeRowList?: boolean;
isCompute?: boolean;
}
export interface IDrawImagePayload {
@ -19,17 +19,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 {
@ -37,4 +38,20 @@ export interface IPositionContext {
tdId?: string;
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[];
}

@ -111,4 +111,10 @@ export function mergeObject<T>(source: T, target: T): T {
target.push(...source)
}
return target
}
export function nextTick(fn: Function) {
setTimeout(() => {
fn()
}, 0)
}
Loading…
Cancel
Save