feat:render header element

pr675
Hufe921 3 years ago
parent 8eee356787
commit da2dfd3a16

@ -2,7 +2,7 @@ import { version } from '../../../../package.json'
import { ZERO } from '../../dataset/constant/Common' import { ZERO } from '../../dataset/constant/Common'
import { RowFlex } from '../../dataset/enum/Row' import { RowFlex } from '../../dataset/enum/Row'
import { IDrawOption, IDrawRowPayload, IPainterOptions } from '../../interface/Draw' 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 { IElement, IElementMetrics, IElementPosition, IElementFillRect, IElementStyle } from '../../interface/Element'
import { IRow, IRowElement } from '../../interface/Row' import { IRow, IRowElement } from '../../interface/Row'
import { deepClone, getUUID, nextTick } from '../../utils' import { deepClone, getUUID, nextTick } from '../../utils'
@ -61,7 +61,9 @@ export class Draw {
private mode: EditorMode private mode: EditorMode
private options: DeepRequired<IEditorOption> private options: DeepRequired<IEditorOption>
private position: Position private position: Position
private headerElementList: IElement[]
private elementList: IElement[] private elementList: IElement[]
private footerElementList: IElement[]
private listener: Listener private listener: Listener
private i18n: I18n private i18n: I18n
@ -110,7 +112,7 @@ export class Draw {
constructor( constructor(
rootContainer: HTMLElement, rootContainer: HTMLElement,
options: DeepRequired<IEditorOption>, options: DeepRequired<IEditorOption>,
elementList: IElement[], data: IEditorDrawData,
listener: Listener listener: Listener
) { ) {
this.container = this._wrapContainer(rootContainer) this.container = this._wrapContainer(rootContainer)
@ -119,7 +121,9 @@ export class Draw {
this.pageNo = 0 this.pageNo = 0
this.mode = options.mode this.mode = options.mode
this.options = options this.options = options
this.elementList = elementList this.headerElementList = data.header || []
this.elementList = data.main
this.footerElementList = data.footer || []
this.listener = listener this.listener = listener
this._formatContainer() this._formatContainer()
@ -343,6 +347,10 @@ export class Draw {
return this.range return this.range
} }
public getHeaderElementList(): IElement[] {
return this.headerElementList
}
public getElementList(): IElement[] { public getElementList(): IElement[] {
const positionContext = this.position.getPositionContext() const positionContext = this.position.getPositionContext()
if (positionContext.isTable) { if (positionContext.isTable) {
@ -352,6 +360,10 @@ export class Draw {
return this.elementList return this.elementList
} }
public getFooterElementList(): IElement[] {
return this.footerElementList
}
public insertElementList(payload: IElement[]) { public insertElementList(payload: IElement[]) {
if (!payload.length) return if (!payload.length) return
const isPartRangeInControlOutside = this.control.isPartRangeInControlOutside() const isPartRangeInControlOutside = this.control.isPartRangeInControlOutside()
@ -596,15 +608,17 @@ export class Draw {
public getValue(): IEditorResult { 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 { return {
version, version,
width, width,
height, height,
margins, margins,
header: header.data ? header : undefined,
watermark: watermark.data ? watermark : undefined, watermark: watermark.data ? watermark : undefined,
data data
} }
@ -670,7 +684,7 @@ export class Draw {
return `${el.italic ? 'italic ' : ''}${el.bold ? 'bold ' : ''}${size * scale}px ${font}` 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 { defaultSize, defaultRowMargin, scale, tdPadding, defaultTabWidth } = this.options
const defaultBasicRowMarginHeight = this.getDefaultBasicRowMarginHeight() const defaultBasicRowMarginHeight = this.getDefaultBasicRowMarginHeight()
const canvas = document.createElement('canvas') const canvas = document.createElement('canvas')
@ -726,7 +740,7 @@ export class Draw {
let maxTrHeight = 0 let maxTrHeight = 0
for (let d = 0; d < tr.tdList.length; d++) { for (let d = 0; d < tr.tdList.length; d++) {
const td = tr.tdList[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) const rowHeight = rowList.reduce((pre, cur) => pre + cur.height, 0)
td.rowList = rowList td.rowList = rowList
// 移除缩放导致的行高变化-渲染时会进行缩放调整 // 移除缩放导致的行高变化-渲染时会进行缩放调整
@ -964,7 +978,7 @@ export class Draw {
this.textParticle.complete() this.textParticle.complete()
} }
private _drawRow(ctx: CanvasRenderingContext2D, payload: IDrawRowPayload) { public drawRow(ctx: CanvasRenderingContext2D, payload: IDrawRowPayload) {
const { rowList, pageNo, positionList, startIndex } = payload const { rowList, pageNo, positionList, startIndex } = payload
const { scale, tdPadding } = this.options const { scale, tdPadding } = this.options
const { isCrossRowCol, tableId } = this.range.getRange() const { isCrossRowCol, tableId } = this.range.getRange()
@ -1104,7 +1118,7 @@ export class Draw {
const tr = element.trList![t] const tr = element.trList![t]
for (let d = 0; d < tr.tdList!.length; d++) { for (let d = 0; d < tr.tdList!.length; d++) {
const td = tr.tdList[d] const td = tr.tdList[d]
this._drawRow(ctx, { this.drawRow(ctx, {
positionList: td.positionList!, positionList: td.positionList!,
rowList: td.rowList!, rowList: td.rowList!,
pageNo, pageNo,
@ -1147,7 +1161,7 @@ export class Draw {
this.margin.render(ctx, pageNo) this.margin.render(ctx, pageNo)
// 渲染元素 // 渲染元素
const index = rowList[0].startIndex const index = rowList[0].startIndex
this._drawRow(ctx, { this.drawRow(ctx, {
positionList, positionList,
rowList, rowList,
pageNo, pageNo,
@ -1175,6 +1189,7 @@ export class Draw {
entries.forEach(entry => { entries.forEach(entry => {
if (entry.isIntersecting) { if (entry.isIntersecting) {
const index = Number((<HTMLCanvasElement>entry.target).dataset.index) const index = Number((<HTMLCanvasElement>entry.target).dataset.index)
this.header.render(this.ctxList[index])
this._drawPage(positionList, this.pageRowList[index], index) this._drawPage(positionList, this.pageRowList[index], index)
} }
}) })
@ -1203,8 +1218,10 @@ export class Draw {
const innerWidth = this.getInnerWidth() const innerWidth = this.getInnerWidth()
// 计算文档信息 // 计算文档信息
if (isCompute) { if (isCompute) {
// 页眉信息
this.header.compute()
// 行信息 // 行信息
this.rowList = this._computeRowList(innerWidth, this.elementList) this.rowList = this.computeRowList(innerWidth, this.elementList)
// 页面信息 // 页面信息
this.pageRowList = this._computePageList() this.pageRowList = this._computePageList()
// 位置信息 // 位置信息

@ -1,31 +1,72 @@
import { DeepRequired } from '../../../interface/Common' import { DeepRequired } from '../../../interface/Common'
import { IEditorOption } from '../../../interface/Editor' 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' import { Draw } from '../Draw'
export class Header { export class Header {
private draw: Draw private draw: Draw
private position: Position
private options: DeepRequired<IEditorOption> private options: DeepRequired<IEditorOption>
private elementList: IElement[]
private rowList: IRow[]
private positionList: IElementPosition[]
constructor(draw: Draw) { constructor(draw: Draw) {
this.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) { public render(ctx: CanvasRenderingContext2D) {
const { header: { data, size, color, font }, scale } = this.options const innerWidth = this.draw.getInnerWidth()
if (!data) return this.draw.drawRow(ctx, {
const width = this.draw.getWidth() positionList: this.positionList,
const top = this.draw.getHeaderTop() rowList: this.rowList,
ctx.save() pageNo: 0,
ctx.fillStyle = color startIndex: 0,
ctx.font = `${size! * scale}px ${font}` innerWidth
// 文字长度 })
const textWidth = ctx.measureText(`${data}`).width
// 偏移量
const left = (width - textWidth) / 2
ctx.fillText(`${data}`, left < 0 ? 0 : left, top)
ctx.restore()
} }
} }

@ -46,7 +46,7 @@ export class Position {
this.positionList = payload this.positionList = payload
} }
private computePageRowPosition(payload: IComputePageRowPositionPayload): IComputePageRowPositionResult { public computePageRowPosition(payload: IComputePageRowPositionPayload): IComputePageRowPositionResult {
const { positionList, rowList, pageNo, startX, startY, startIndex, innerWidth } = payload const { positionList, rowList, pageNo, startX, startY, startIndex, innerWidth } = payload
const { scale, tdPadding } = this.options const { scale, tdPadding } = this.options
let x = startX let x = startX

@ -1,8 +1,7 @@
import { IHeader } from '../../interface/Header' import { IHeader } from '../../interface/Header'
import { HeaderMaxHeightRatio } from '../enum/Header'
export const defaultHeaderOption: Readonly<Required<IHeader>> = { export const defaultHeaderOption: Readonly<Required<IHeader>> = {
data: '', top: -50,
color: '#AAAAAA', maxHeightRadio: HeaderMaxHeightRatio.HALF
size: 14,
font: 'Yahei'
} }

@ -18,6 +18,12 @@ export enum EditorMode {
READONLY = 'readonly' READONLY = 'readonly'
} }
export enum EditorZone {
HEADER = 'header',
MAIN = 'main',
FOOTER = 'footer'
}
export enum PageMode { export enum PageMode {
PAGING = 'paging', PAGING = 'paging',
CONTINUITY = 'continuity' CONTINUITY = 'continuity'

@ -0,0 +1,5 @@
export enum HeaderMaxHeightRatio {
HALF = 'half',
ONE_THIRD = 'one-third',
QUARTER = 'quarter'
}

@ -1,5 +1,5 @@
import './assets/css/index.css' import './assets/css/index.css'
import { IEditorOption, IEditorResult } from './interface/Editor' import { IEditorData, IEditorOption, IEditorResult } from './interface/Editor'
import { IElement } from './interface/Element' import { IElement } from './interface/Element'
import { Draw } from './core/draw/Draw' import { Draw } from './core/draw/Draw'
import { Command } from './core/command/Command' import { Command } from './core/command/Command'
@ -39,7 +39,7 @@ export default class Editor {
public register: Register public register: Register
public destroy: Function public destroy: Function
constructor(container: HTMLDivElement, elementList: IElement[], options: IEditorOption = {}) { constructor(container: HTMLDivElement, data: IEditorData, options: IEditorOption = {}) {
const headerOptions: Required<IHeader> = { const headerOptions: Required<IHeader> = {
...defaultHeaderOption, ...defaultHeaderOption,
...options.header ...options.header
@ -103,13 +103,33 @@ export default class Editor {
checkbox: checkboxOptions, checkbox: checkboxOptions,
cursor: cursorOptions 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 editorOptions
}) })
// 监听 // 监听
this.listener = new Listener() 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)) this.command = new Command(new CommandAdapt(draw))
// 菜单 // 菜单

@ -7,6 +7,14 @@ import { IHeader } from './Header'
import { IMargin } from './Margin' import { IMargin } from './Margin'
import { IWatermark } from './Watermark' import { IWatermark } from './Watermark'
export interface IEditorDrawData {
header?: IElement[];
main: IElement[];
footer?: IElement[];
}
export type IEditorData = IEditorDrawData | IElement[]
export interface IEditorOption { export interface IEditorOption {
mode?: EditorMode; mode?: EditorMode;
defaultType?: string; defaultType?: string;
@ -54,7 +62,6 @@ export interface IEditorResult {
width: number; width: number;
height: number; height: number;
margins: IMargin; margins: IMargin;
header?: IHeader;
watermark?: IWatermark; watermark?: IWatermark;
data: IElement[]; data: IEditorData;
} }

@ -1,6 +1,6 @@
import { HeaderMaxHeightRatio } from '../dataset/enum/Header'
export interface IHeader { export interface IHeader {
data: string; top?: number;
color?: string; maxHeightRadio?: HeaderMaxHeightRatio;
size?: number;
font?: string;
} }

@ -1,7 +1,7 @@
import { data, options } from './mock' import { data, options } from './mock'
import './style.css' import './style.css'
import prism from 'prismjs' 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 { Dialog } from './components/dialog/Dialog'
import { formatPrismToken } from './utils/prism' import { formatPrismToken } from './utils/prism'
import { Signature } from './components/signature/Signature' import { Signature } from './components/signature/Signature'
@ -11,7 +11,19 @@ window.onload = function () {
// 1. 初始化编辑器 // 1. 初始化编辑器
const container = document.querySelector<HTMLDivElement>('.editor')! 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) console.log('实例: ', instance)
// cypress使用 // cypress使用
Reflect.set(window, 'editor', instance) Reflect.set(window, 'editor', instance)

@ -312,9 +312,6 @@ export const data: IElement[] = elementList
export const options: IEditorOption = { export const options: IEditorOption = {
margins: [100, 120, 100, 120], margins: [100, 120, 100, 120],
header: {
data: '人民医院门诊'
},
watermark: { watermark: {
data: 'CANVAS-EDITOR', data: 'CANVAS-EDITOR',
size: 120 size: 120

Loading…
Cancel
Save