feat:render header element
This commit is contained in:
@@ -2,7 +2,7 @@ import { version } from '../../../../package.json'
|
||||
import { ZERO } from '../../dataset/constant/Common'
|
||||
import { RowFlex } from '../../dataset/enum/Row'
|
||||
import { IDrawOption, IDrawRowPayload, IPainterOptions } from '../../interface/Draw'
|
||||
import { IEditorOption, IEditorResult } from '../../interface/Editor'
|
||||
import { IEditorDrawData, IEditorOption, IEditorResult } from '../../interface/Editor'
|
||||
import { IElement, IElementMetrics, IElementPosition, IElementFillRect, IElementStyle } from '../../interface/Element'
|
||||
import { IRow, IRowElement } from '../../interface/Row'
|
||||
import { deepClone, getUUID, nextTick } from '../../utils'
|
||||
@@ -61,7 +61,9 @@ export class Draw {
|
||||
private mode: EditorMode
|
||||
private options: DeepRequired<IEditorOption>
|
||||
private position: Position
|
||||
private headerElementList: IElement[]
|
||||
private elementList: IElement[]
|
||||
private footerElementList: IElement[]
|
||||
private listener: Listener
|
||||
|
||||
private i18n: I18n
|
||||
@@ -110,7 +112,7 @@ export class Draw {
|
||||
constructor(
|
||||
rootContainer: HTMLElement,
|
||||
options: DeepRequired<IEditorOption>,
|
||||
elementList: IElement[],
|
||||
data: IEditorDrawData,
|
||||
listener: Listener
|
||||
) {
|
||||
this.container = this._wrapContainer(rootContainer)
|
||||
@@ -119,7 +121,9 @@ export class Draw {
|
||||
this.pageNo = 0
|
||||
this.mode = options.mode
|
||||
this.options = options
|
||||
this.elementList = elementList
|
||||
this.headerElementList = data.header || []
|
||||
this.elementList = data.main
|
||||
this.footerElementList = data.footer || []
|
||||
this.listener = listener
|
||||
|
||||
this._formatContainer()
|
||||
@@ -343,6 +347,10 @@ export class Draw {
|
||||
return this.range
|
||||
}
|
||||
|
||||
public getHeaderElementList(): IElement[] {
|
||||
return this.headerElementList
|
||||
}
|
||||
|
||||
public getElementList(): IElement[] {
|
||||
const positionContext = this.position.getPositionContext()
|
||||
if (positionContext.isTable) {
|
||||
@@ -352,6 +360,10 @@ export class Draw {
|
||||
return this.elementList
|
||||
}
|
||||
|
||||
public getFooterElementList(): IElement[] {
|
||||
return this.footerElementList
|
||||
}
|
||||
|
||||
public insertElementList(payload: IElement[]) {
|
||||
if (!payload.length) return
|
||||
const isPartRangeInControlOutside = this.control.isPartRangeInControlOutside()
|
||||
@@ -596,15 +608,17 @@ export class Draw {
|
||||
|
||||
public getValue(): IEditorResult {
|
||||
// 配置
|
||||
const { width, height, margins, watermark, header } = this.options
|
||||
const { width, height, margins, watermark } = this.options
|
||||
// 数据
|
||||
const data = zipElementList(this.elementList)
|
||||
const data: IEditorDrawData = {
|
||||
header: zipElementList(this.headerElementList),
|
||||
main: zipElementList(this.elementList)
|
||||
}
|
||||
return {
|
||||
version,
|
||||
width,
|
||||
height,
|
||||
margins,
|
||||
header: header.data ? header : undefined,
|
||||
watermark: watermark.data ? watermark : undefined,
|
||||
data
|
||||
}
|
||||
@@ -670,7 +684,7 @@ export class Draw {
|
||||
return `${el.italic ? 'italic ' : ''}${el.bold ? 'bold ' : ''}${size * scale}px ${font}`
|
||||
}
|
||||
|
||||
private _computeRowList(innerWidth: number, elementList: IElement[]) {
|
||||
public computeRowList(innerWidth: number, elementList: IElement[]) {
|
||||
const { defaultSize, defaultRowMargin, scale, tdPadding, defaultTabWidth } = this.options
|
||||
const defaultBasicRowMarginHeight = this.getDefaultBasicRowMarginHeight()
|
||||
const canvas = document.createElement('canvas')
|
||||
@@ -726,7 +740,7 @@ export class Draw {
|
||||
let maxTrHeight = 0
|
||||
for (let d = 0; d < tr.tdList.length; d++) {
|
||||
const td = tr.tdList[d]
|
||||
const rowList = this._computeRowList((td.width! - tdGap) * scale, td.value)
|
||||
const rowList = this.computeRowList((td.width! - tdGap) * scale, td.value)
|
||||
const rowHeight = rowList.reduce((pre, cur) => pre + cur.height, 0)
|
||||
td.rowList = rowList
|
||||
// 移除缩放导致的行高变化-渲染时会进行缩放调整
|
||||
@@ -964,7 +978,7 @@ export class Draw {
|
||||
this.textParticle.complete()
|
||||
}
|
||||
|
||||
private _drawRow(ctx: CanvasRenderingContext2D, payload: IDrawRowPayload) {
|
||||
public drawRow(ctx: CanvasRenderingContext2D, payload: IDrawRowPayload) {
|
||||
const { rowList, pageNo, positionList, startIndex } = payload
|
||||
const { scale, tdPadding } = this.options
|
||||
const { isCrossRowCol, tableId } = this.range.getRange()
|
||||
@@ -1104,7 +1118,7 @@ export class Draw {
|
||||
const tr = element.trList![t]
|
||||
for (let d = 0; d < tr.tdList!.length; d++) {
|
||||
const td = tr.tdList[d]
|
||||
this._drawRow(ctx, {
|
||||
this.drawRow(ctx, {
|
||||
positionList: td.positionList!,
|
||||
rowList: td.rowList!,
|
||||
pageNo,
|
||||
@@ -1147,7 +1161,7 @@ export class Draw {
|
||||
this.margin.render(ctx, pageNo)
|
||||
// 渲染元素
|
||||
const index = rowList[0].startIndex
|
||||
this._drawRow(ctx, {
|
||||
this.drawRow(ctx, {
|
||||
positionList,
|
||||
rowList,
|
||||
pageNo,
|
||||
@@ -1175,6 +1189,7 @@ export class Draw {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
const index = Number((<HTMLCanvasElement>entry.target).dataset.index)
|
||||
this.header.render(this.ctxList[index])
|
||||
this._drawPage(positionList, this.pageRowList[index], index)
|
||||
}
|
||||
})
|
||||
@@ -1203,8 +1218,10 @@ export class Draw {
|
||||
const innerWidth = this.getInnerWidth()
|
||||
// 计算文档信息
|
||||
if (isCompute) {
|
||||
// 页眉信息
|
||||
this.header.compute()
|
||||
// 行信息
|
||||
this.rowList = this._computeRowList(innerWidth, this.elementList)
|
||||
this.rowList = this.computeRowList(innerWidth, this.elementList)
|
||||
// 页面信息
|
||||
this.pageRowList = this._computePageList()
|
||||
// 位置信息
|
||||
|
||||
@@ -1,31 +1,72 @@
|
||||
import { DeepRequired } from '../../../interface/Common'
|
||||
import { IEditorOption } from '../../../interface/Editor'
|
||||
import { IElement, IElementPosition } from '../../../interface/Element'
|
||||
import { IRow } from '../../../interface/Row'
|
||||
import { Position } from '../../position/Position'
|
||||
import { Draw } from '../Draw'
|
||||
|
||||
export class Header {
|
||||
|
||||
private draw: Draw
|
||||
private position: Position
|
||||
private options: DeepRequired<IEditorOption>
|
||||
|
||||
private elementList: IElement[]
|
||||
private rowList: IRow[]
|
||||
private positionList: IElementPosition[]
|
||||
|
||||
constructor(draw: Draw) {
|
||||
this.draw = draw
|
||||
this.options = <DeepRequired<IEditorOption>>draw.getOptions()
|
||||
this.position = draw.getPosition()
|
||||
this.options = draw.getOptions()
|
||||
|
||||
this.elementList = draw.getHeaderElementList()
|
||||
this.rowList = []
|
||||
this.positionList = []
|
||||
}
|
||||
|
||||
public compute() {
|
||||
this._recovery()
|
||||
this._computeRowList()
|
||||
this._computePositionList()
|
||||
}
|
||||
|
||||
private _recovery() {
|
||||
this.rowList = []
|
||||
this.positionList = []
|
||||
}
|
||||
|
||||
private _computeRowList() {
|
||||
const innerWidth = this.draw.getInnerWidth()
|
||||
this.rowList = this.draw.computeRowList(innerWidth, this.elementList)
|
||||
}
|
||||
|
||||
private _computePositionList() {
|
||||
const { header: { top } } = this.options
|
||||
const innerWidth = this.draw.getInnerWidth()
|
||||
const margins = this.draw.getMargins()
|
||||
const startX = margins[3]
|
||||
const startY = margins[0] + top
|
||||
this.position.computePageRowPosition({
|
||||
positionList: this.positionList,
|
||||
rowList: this.rowList,
|
||||
pageNo: 0,
|
||||
startIndex: 0,
|
||||
startX,
|
||||
startY,
|
||||
innerWidth
|
||||
})
|
||||
}
|
||||
|
||||
public render(ctx: CanvasRenderingContext2D) {
|
||||
const { header: { data, size, color, font }, scale } = this.options
|
||||
if (!data) return
|
||||
const width = this.draw.getWidth()
|
||||
const top = this.draw.getHeaderTop()
|
||||
ctx.save()
|
||||
ctx.fillStyle = color
|
||||
ctx.font = `${size! * scale}px ${font}`
|
||||
// 文字长度
|
||||
const textWidth = ctx.measureText(`${data}`).width
|
||||
// 偏移量
|
||||
const left = (width - textWidth) / 2
|
||||
ctx.fillText(`${data}`, left < 0 ? 0 : left, top)
|
||||
ctx.restore()
|
||||
const innerWidth = this.draw.getInnerWidth()
|
||||
this.draw.drawRow(ctx, {
|
||||
positionList: this.positionList,
|
||||
rowList: this.rowList,
|
||||
pageNo: 0,
|
||||
startIndex: 0,
|
||||
innerWidth
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,7 +46,7 @@ export class Position {
|
||||
this.positionList = payload
|
||||
}
|
||||
|
||||
private computePageRowPosition(payload: IComputePageRowPositionPayload): IComputePageRowPositionResult {
|
||||
public computePageRowPosition(payload: IComputePageRowPositionPayload): IComputePageRowPositionResult {
|
||||
const { positionList, rowList, pageNo, startX, startY, startIndex, innerWidth } = payload
|
||||
const { scale, tdPadding } = this.options
|
||||
let x = startX
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { IHeader } from '../../interface/Header'
|
||||
import { HeaderMaxHeightRatio } from '../enum/Header'
|
||||
|
||||
export const defaultHeaderOption: Readonly<Required<IHeader>> = {
|
||||
data: '',
|
||||
color: '#AAAAAA',
|
||||
size: 14,
|
||||
font: 'Yahei'
|
||||
top: -50,
|
||||
maxHeightRadio: HeaderMaxHeightRatio.HALF
|
||||
}
|
||||
@@ -18,6 +18,12 @@ export enum EditorMode {
|
||||
READONLY = 'readonly'
|
||||
}
|
||||
|
||||
export enum EditorZone {
|
||||
HEADER = 'header',
|
||||
MAIN = 'main',
|
||||
FOOTER = 'footer'
|
||||
}
|
||||
|
||||
export enum PageMode {
|
||||
PAGING = 'paging',
|
||||
CONTINUITY = 'continuity'
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export enum HeaderMaxHeightRatio {
|
||||
HALF = 'half',
|
||||
ONE_THIRD = 'one-third',
|
||||
QUARTER = 'quarter'
|
||||
}
|
||||
+24
-4
@@ -1,5 +1,5 @@
|
||||
import './assets/css/index.css'
|
||||
import { IEditorOption, IEditorResult } from './interface/Editor'
|
||||
import { IEditorData, IEditorOption, IEditorResult } from './interface/Editor'
|
||||
import { IElement } from './interface/Element'
|
||||
import { Draw } from './core/draw/Draw'
|
||||
import { Command } from './core/command/Command'
|
||||
@@ -39,7 +39,7 @@ export default class Editor {
|
||||
public register: Register
|
||||
public destroy: Function
|
||||
|
||||
constructor(container: HTMLDivElement, elementList: IElement[], options: IEditorOption = {}) {
|
||||
constructor(container: HTMLDivElement, data: IEditorData, options: IEditorOption = {}) {
|
||||
const headerOptions: Required<IHeader> = {
|
||||
...defaultHeaderOption,
|
||||
...options.header
|
||||
@@ -103,13 +103,33 @@ export default class Editor {
|
||||
checkbox: checkboxOptions,
|
||||
cursor: cursorOptions
|
||||
}
|
||||
formatElementList(elementList, {
|
||||
// 数据处理
|
||||
let headerElementList: IElement[] = []
|
||||
let mainElementList: IElement[] = []
|
||||
if (Array.isArray(data)) {
|
||||
mainElementList = data
|
||||
} else {
|
||||
headerElementList = data.header || []
|
||||
mainElementList = data.main
|
||||
}
|
||||
formatElementList(headerElementList, {
|
||||
editorOptions
|
||||
})
|
||||
formatElementList(mainElementList, {
|
||||
editorOptions
|
||||
})
|
||||
// 监听
|
||||
this.listener = new Listener()
|
||||
// 启动
|
||||
const draw = new Draw(container, editorOptions, elementList, this.listener)
|
||||
const draw = new Draw(
|
||||
container,
|
||||
editorOptions,
|
||||
{
|
||||
header: headerElementList,
|
||||
main: mainElementList
|
||||
},
|
||||
this.listener
|
||||
)
|
||||
// 命令
|
||||
this.command = new Command(new CommandAdapt(draw))
|
||||
// 菜单
|
||||
|
||||
@@ -7,6 +7,14 @@ import { IHeader } from './Header'
|
||||
import { IMargin } from './Margin'
|
||||
import { IWatermark } from './Watermark'
|
||||
|
||||
export interface IEditorDrawData {
|
||||
header?: IElement[];
|
||||
main: IElement[];
|
||||
footer?: IElement[];
|
||||
}
|
||||
|
||||
export type IEditorData = IEditorDrawData | IElement[]
|
||||
|
||||
export interface IEditorOption {
|
||||
mode?: EditorMode;
|
||||
defaultType?: string;
|
||||
@@ -54,7 +62,6 @@ export interface IEditorResult {
|
||||
width: number;
|
||||
height: number;
|
||||
margins: IMargin;
|
||||
header?: IHeader;
|
||||
watermark?: IWatermark;
|
||||
data: IElement[];
|
||||
data: IEditorData;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { HeaderMaxHeightRatio } from '../dataset/enum/Header'
|
||||
|
||||
export interface IHeader {
|
||||
data: string;
|
||||
color?: string;
|
||||
size?: number;
|
||||
font?: string;
|
||||
top?: number;
|
||||
maxHeightRadio?: HeaderMaxHeightRatio;
|
||||
}
|
||||
+14
-2
@@ -1,7 +1,7 @@
|
||||
import { data, options } from './mock'
|
||||
import './style.css'
|
||||
import prism from 'prismjs'
|
||||
import Editor, { BlockType, Command, ControlType, EditorMode, ElementType, IBlock, IElement, KeyMap, PageMode, PaperDirection } from './editor'
|
||||
import Editor, { BlockType, Command, ControlType, EditorMode, ElementType, IBlock, IElement, KeyMap, PageMode, PaperDirection, RowFlex } from './editor'
|
||||
import { Dialog } from './components/dialog/Dialog'
|
||||
import { formatPrismToken } from './utils/prism'
|
||||
import { Signature } from './components/signature/Signature'
|
||||
@@ -11,7 +11,19 @@ window.onload = function () {
|
||||
|
||||
// 1. 初始化编辑器
|
||||
const container = document.querySelector<HTMLDivElement>('.editor')!
|
||||
const instance = new Editor(container, <IElement[]>data, options)
|
||||
const instance = new Editor(
|
||||
container,
|
||||
{
|
||||
header: [{
|
||||
value: '人民医院门诊',
|
||||
size: 14,
|
||||
color: '#AAAAAA',
|
||||
rowFlex: RowFlex.CENTER
|
||||
}],
|
||||
main: <IElement[]>data
|
||||
},
|
||||
options
|
||||
)
|
||||
console.log('实例: ', instance)
|
||||
// cypress使用
|
||||
Reflect.set(window, 'editor', instance)
|
||||
|
||||
@@ -312,9 +312,6 @@ export const data: IElement[] = elementList
|
||||
|
||||
export const options: IEditorOption = {
|
||||
margins: [100, 120, 100, 120],
|
||||
header: {
|
||||
data: '人民医院门诊'
|
||||
},
|
||||
watermark: {
|
||||
data: 'CANVAS-EDITOR',
|
||||
size: 120
|
||||
|
||||
Reference in New Issue
Block a user