feat: add page footer
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
|
||||
.ce-resizer-selection>div {
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
box-shadow: 0 1px 4px 0 rgb(0 0 0 / 30%);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.ce-header-indicator>div {
|
||||
.ce-zone-indicator>div {
|
||||
padding: 3px 6px;
|
||||
color: #000000;
|
||||
font-size: 12px;
|
||||
@@ -7,27 +7,28 @@
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
.ce-header-indicator-border__top,
|
||||
.ce-header-indicator-border__bottom,
|
||||
.ce-header-indicator-border__left,
|
||||
.ce-header-indicator-border__right {
|
||||
.ce-zone-indicator-border__top,
|
||||
.ce-zone-indicator-border__bottom,
|
||||
.ce-zone-indicator-border__left,
|
||||
.ce-zone-indicator-border__right {
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.ce-header-indicator-border__top {
|
||||
.ce-zone-indicator-border__top {
|
||||
border-top: 2px dashed rgb(238, 238, 238);
|
||||
}
|
||||
|
||||
.ce-header-indicator-border__bottom {
|
||||
.ce-zone-indicator-border__bottom {
|
||||
border-top: 2px dashed rgb(238, 238, 238);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ce-header-indicator-border__left {
|
||||
.ce-zone-indicator-border__left {
|
||||
border-left: 2px dashed rgb(238, 238, 238);
|
||||
}
|
||||
|
||||
.ce-header-indicator-border__right {
|
||||
.ce-zone-indicator-border__right {
|
||||
border-right: 2px dashed rgb(238, 238, 238);
|
||||
}
|
||||
@@ -51,6 +51,7 @@ import { EDITOR_COMPONENT, EDITOR_PREFIX } from '../../dataset/constant/Editor'
|
||||
import { I18n } from '../i18n/I18n'
|
||||
import { ImageObserver } from '../observer/ImageObserver'
|
||||
import { Zone } from '../zone/Zone'
|
||||
import { Footer } from './frame/Footer'
|
||||
|
||||
export class Draw {
|
||||
|
||||
@@ -89,6 +90,7 @@ export class Draw {
|
||||
private pageNumber: PageNumber
|
||||
private waterMark: Watermark
|
||||
private header: Header
|
||||
private footer: Footer
|
||||
private hyperlinkParticle: HyperlinkParticle
|
||||
private dateParticle: DateParticle
|
||||
private separatorParticle: SeparatorParticle
|
||||
@@ -152,6 +154,7 @@ export class Draw {
|
||||
this.pageNumber = new PageNumber(this)
|
||||
this.waterMark = new Watermark(this)
|
||||
this.header = new Header(this)
|
||||
this.footer = new Footer(this)
|
||||
this.hyperlinkParticle = new HyperlinkParticle(this)
|
||||
this.dateParticle = new DateParticle(this)
|
||||
this.separatorParticle = new SeparatorParticle()
|
||||
@@ -217,9 +220,14 @@ export class Draw {
|
||||
|
||||
public getMainHeight(): number {
|
||||
const pageHeight = this.getHeight()
|
||||
return pageHeight - this.getMainOuterHeight()
|
||||
}
|
||||
|
||||
public getMainOuterHeight(): number {
|
||||
const margins = this.getMargins()
|
||||
const extraHeight = this.header.getExtraHeight()
|
||||
return pageHeight - margins[0] - margins[2] - extraHeight
|
||||
const headerExtraHeight = this.header.getExtraHeight()
|
||||
const footerExtraHeight = this.footer.getExtraHeight()
|
||||
return margins[0] + margins[2] + headerExtraHeight + footerExtraHeight
|
||||
}
|
||||
|
||||
public getCanvasWidth(pageNo = -1): number {
|
||||
@@ -385,9 +393,13 @@ export class Draw {
|
||||
|
||||
public getOriginalElementList() {
|
||||
const zoneManager = this.getZone()
|
||||
return zoneManager.isHeaderActive()
|
||||
? this.header.getElementList()
|
||||
: this.elementList
|
||||
if (zoneManager.isHeaderActive()) {
|
||||
return this.header.getElementList()
|
||||
}
|
||||
if (zoneManager.isFooterActive()) {
|
||||
return this.footer.getElementList()
|
||||
}
|
||||
return this.elementList
|
||||
}
|
||||
|
||||
public getOriginalMainElementList(): IElement[] {
|
||||
@@ -468,6 +480,10 @@ export class Draw {
|
||||
return this.header
|
||||
}
|
||||
|
||||
public getFooter(): Footer {
|
||||
return this.footer
|
||||
}
|
||||
|
||||
public getHyperlinkParticle(): HyperlinkParticle {
|
||||
return this.hyperlinkParticle
|
||||
}
|
||||
@@ -646,7 +662,8 @@ export class Draw {
|
||||
// 数据
|
||||
const data: IEditorData = {
|
||||
header: zipElementList(this.headerElementList),
|
||||
main: zipElementList(this.elementList)
|
||||
main: zipElementList(this.elementList),
|
||||
footer: zipElementList(this.footerElementList)
|
||||
}
|
||||
return {
|
||||
version,
|
||||
@@ -803,10 +820,8 @@ export class Draw {
|
||||
metrics.boundingBoxDescent = elementHeight
|
||||
metrics.boundingBoxAscent = 0
|
||||
// 表格分页处理(拆分表格)
|
||||
const margins = this.getMargins()
|
||||
const height = this.getHeight()
|
||||
const headerExtraHeight = this.header.getExtraHeight()
|
||||
const marginHeight = margins[0] + margins[2] + headerExtraHeight
|
||||
const marginHeight = this.getMainOuterHeight()
|
||||
let curPagePreHeight = marginHeight
|
||||
for (let r = 0; r < rowList.length; r++) {
|
||||
const row = rowList[r]
|
||||
@@ -973,9 +988,7 @@ export class Draw {
|
||||
const pageRowList: IRow[][] = [[]]
|
||||
const { pageMode } = this.options
|
||||
const height = this.getHeight()
|
||||
const margins = this.getMargins()
|
||||
const headerExtraHeight = this.header.getExtraHeight()
|
||||
const marginHeight = margins[0] + margins[2] + headerExtraHeight
|
||||
const marginHeight = this.getMainOuterHeight()
|
||||
let pageHeight = marginHeight
|
||||
let pageNo = 0
|
||||
if (pageMode === PageMode.CONTINUITY) {
|
||||
@@ -1196,8 +1209,8 @@ export class Draw {
|
||||
const { inactiveAlpha, pageMode } = this.options
|
||||
const innerWidth = this.getInnerWidth()
|
||||
const ctx = this.ctxList[pageNo]
|
||||
// 判断当前激活区域-激活页眉时主题元素透明度降低
|
||||
ctx.globalAlpha = this.zone.isHeaderActive() ? inactiveAlpha : 1
|
||||
// 判断当前激活区域-非正文区域时元素透明度降低
|
||||
ctx.globalAlpha = !this.zone.isMainActive() ? inactiveAlpha : 1
|
||||
this._clearPage(pageNo)
|
||||
// 绘制背景
|
||||
this.background.render(ctx)
|
||||
@@ -1218,6 +1231,8 @@ export class Draw {
|
||||
this.header.render(ctx, pageNo)
|
||||
// 绘制页码
|
||||
this.pageNumber.render(ctx, pageNo)
|
||||
// 绘制页脚
|
||||
this.footer.render(ctx, pageNo)
|
||||
// 搜索匹配绘制
|
||||
if (this.search.getSearchKeyword()) {
|
||||
this.search.render(ctx, pageNo)
|
||||
@@ -1236,7 +1251,6 @@ export class Draw {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
const index = Number((<HTMLCanvasElement>entry.target).dataset.index)
|
||||
this.header.render(this.ctxList[index], index)
|
||||
this._drawPage({
|
||||
elementList,
|
||||
positionList,
|
||||
@@ -1278,6 +1292,8 @@ export class Draw {
|
||||
if (isCompute) {
|
||||
// 页眉信息
|
||||
this.header.compute()
|
||||
// 页脚信息
|
||||
this.footer.compute()
|
||||
// 行信息
|
||||
this.rowList = this.computeRowList(innerWidth, this.elementList)
|
||||
// 页面信息
|
||||
@@ -1338,6 +1354,7 @@ export class Draw {
|
||||
const self = this
|
||||
const oldElementList = deepClone(this.elementList)
|
||||
const oldHeaderElementList = deepClone(this.header.getElementList())
|
||||
const oldFooterElementList = deepClone(this.footer.getElementList())
|
||||
const { startIndex, endIndex } = this.range.getRange()
|
||||
const pageNo = this.pageNo
|
||||
const oldPositionContext = deepClone(positionContext)
|
||||
@@ -1345,8 +1362,9 @@ export class Draw {
|
||||
this.historyManager.execute(function () {
|
||||
self.zone.setZone(zone)
|
||||
self.setPageNo(pageNo)
|
||||
self.position.setPositionContext(oldPositionContext)
|
||||
self.header.setElementList(oldHeaderElementList)
|
||||
self.position.setPositionContext(deepClone(oldPositionContext))
|
||||
self.header.setElementList(deepClone(oldHeaderElementList))
|
||||
self.footer.setElementList(deepClone(oldFooterElementList))
|
||||
self.elementList = deepClone(oldElementList)
|
||||
self.range.setRange(startIndex, endIndex)
|
||||
self.render({ curIndex, isSubmitHistory: false })
|
||||
@@ -1359,8 +1377,8 @@ export class Draw {
|
||||
this.tableTool.render()
|
||||
}
|
||||
// 页眉指示器重新渲染
|
||||
if (isCompute && this.zone.isHeaderActive()) {
|
||||
this.zone.drawHeaderZoneIndicator()
|
||||
if (isCompute && !this.zone.isMainActive()) {
|
||||
this.zone.drawZoneIndicator()
|
||||
}
|
||||
// 页面尺寸改变
|
||||
if (this.listener.pageSizeChange) {
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
import { maxHeightRadioMapping } from '../../../dataset/constant/Common'
|
||||
import { EditorZone } from '../../../dataset/enum/Editor'
|
||||
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 Footer {
|
||||
|
||||
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.position = draw.getPosition()
|
||||
this.options = draw.getOptions()
|
||||
|
||||
this.elementList = draw.getFooterElementList()
|
||||
this.rowList = []
|
||||
this.positionList = []
|
||||
}
|
||||
|
||||
public setElementList(elementList: IElement[]) {
|
||||
this.elementList = elementList
|
||||
}
|
||||
|
||||
public getElementList(): IElement[] {
|
||||
return this.elementList
|
||||
}
|
||||
|
||||
public getPositionList(): IElementPosition[] {
|
||||
return 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 footerBottom = this.getFooterBottom()
|
||||
const innerWidth = this.draw.getInnerWidth()
|
||||
const margins = this.draw.getMargins()
|
||||
const startX = margins[3]
|
||||
// 页面高度 - 页脚顶部距离页面底部高度
|
||||
const pageHeight = this.draw.getHeight()
|
||||
const footerHeight = this.getHeight()
|
||||
const startY = pageHeight - footerBottom - footerHeight
|
||||
this.position.computePageRowPosition({
|
||||
positionList: this.positionList,
|
||||
rowList: this.rowList,
|
||||
pageNo: 0,
|
||||
startIndex: 0,
|
||||
startX,
|
||||
startY,
|
||||
innerWidth
|
||||
})
|
||||
}
|
||||
|
||||
public getFooterBottom(): number {
|
||||
const { footer: { bottom }, scale } = this.options
|
||||
return Math.floor(bottom * scale)
|
||||
}
|
||||
|
||||
public getMaxHeight(): number {
|
||||
const { footer: { maxHeightRadio } } = this.options
|
||||
const height = this.draw.getHeight()
|
||||
return Math.floor(height * maxHeightRadioMapping[maxHeightRadio])
|
||||
}
|
||||
|
||||
public getHeight(): number {
|
||||
const maxHeight = this.getMaxHeight()
|
||||
const rowHeight = this.getRowHeight()
|
||||
return rowHeight > maxHeight ? maxHeight : rowHeight
|
||||
}
|
||||
|
||||
public getRowHeight(): number {
|
||||
return this.rowList.reduce((pre, cur) => pre + cur.height, 0)
|
||||
}
|
||||
|
||||
public getExtraHeight(): number {
|
||||
// 页脚下边距 + 实际高 - 页面上边距
|
||||
const margins = this.draw.getMargins()
|
||||
const footerHeight = this.getHeight()
|
||||
const footerBottom = this.getFooterBottom()
|
||||
const extraHeight = footerBottom + footerHeight - margins[2]
|
||||
return extraHeight <= 0 ? 0 : extraHeight
|
||||
}
|
||||
|
||||
public render(ctx: CanvasRenderingContext2D, pageNo: number) {
|
||||
ctx.globalAlpha = 1
|
||||
const innerWidth = this.draw.getInnerWidth()
|
||||
const maxHeight = this.getMaxHeight()
|
||||
// 超出最大高度不渲染
|
||||
const rowList: IRow[] = []
|
||||
let curRowHeight = 0
|
||||
for (let r = 0; r < this.rowList.length; r++) {
|
||||
const row = this.rowList[r]
|
||||
if (curRowHeight + row.height > maxHeight) {
|
||||
break
|
||||
}
|
||||
rowList.push(row)
|
||||
curRowHeight += row.height
|
||||
}
|
||||
this.draw.drawRow(ctx, {
|
||||
elementList: this.elementList,
|
||||
positionList: this.positionList,
|
||||
rowList,
|
||||
pageNo,
|
||||
startIndex: 0,
|
||||
innerWidth,
|
||||
zone: EditorZone.FOOTER
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { maxHeightRadioMapping } from '../../../dataset/constant/Header'
|
||||
import { maxHeightRadioMapping } from '../../../dataset/constant/Common'
|
||||
import { EditorZone } from '../../../dataset/enum/Editor'
|
||||
import { DeepRequired } from '../../../interface/Common'
|
||||
import { IEditorOption } from '../../../interface/Editor'
|
||||
|
||||
@@ -32,7 +32,7 @@ export class PageNumber {
|
||||
const margins = this.draw.getMargins()
|
||||
const { width: textWidth } = ctx.measureText(text)
|
||||
if (rowFlex === RowFlex.CENTER) {
|
||||
x = (width + textWidth) / 2
|
||||
x = (width - textWidth) / 2
|
||||
} else if (rowFlex === RowFlex.RIGHT) {
|
||||
x = width - textWidth - margins[1]
|
||||
} else {
|
||||
|
||||
@@ -262,9 +262,9 @@ export function keydown(evt: KeyboardEvent, host: CanvasEvent) {
|
||||
} else if (evt.key === KeyMap.ESC) {
|
||||
// 退出格式刷
|
||||
host.clearPainterStyle()
|
||||
// 退出页眉编辑
|
||||
// 退出页眉页脚编辑
|
||||
const zoneManager = draw.getZone()
|
||||
if (zoneManager.isHeaderActive()) {
|
||||
if (!zoneManager.isMainActive()) {
|
||||
zoneManager.setZone(EditorZone.MAIN)
|
||||
}
|
||||
evt.preventDefault()
|
||||
|
||||
@@ -48,8 +48,15 @@ export class Position {
|
||||
|
||||
public getOriginalPositionList(): IElementPosition[] {
|
||||
const zoneManager = this.draw.getZone()
|
||||
const header = this.draw.getHeader()
|
||||
return zoneManager.isHeaderActive() ? header.getPositionList() : this.positionList
|
||||
if (zoneManager.isHeaderActive()) {
|
||||
const header = this.draw.getHeader()
|
||||
return header.getPositionList()
|
||||
}
|
||||
if (zoneManager.isFooterActive()) {
|
||||
const footer = this.draw.getFooter()
|
||||
return footer.getPositionList()
|
||||
}
|
||||
return this.positionList
|
||||
}
|
||||
|
||||
public getOriginalMainPositionList(): IElementPosition[] {
|
||||
@@ -209,7 +216,8 @@ export class Position {
|
||||
}
|
||||
const zoneManager = this.draw.getZone()
|
||||
const curPageNo = this.draw.getPageNo()
|
||||
const positionNo = zoneManager.isMainActive() ? curPageNo : 0
|
||||
const isMainActive = zoneManager.isMainActive()
|
||||
const positionNo = isMainActive ? curPageNo : 0
|
||||
for (let j = 0; j < positionList.length; j++) {
|
||||
const { index, pageNo, coordinate: { leftTop, rightTop, leftBottom } } = positionList[j]
|
||||
if (positionNo !== pageNo) continue
|
||||
@@ -306,15 +314,15 @@ export class Position {
|
||||
}
|
||||
}
|
||||
// 判断所属行是否存在元素
|
||||
const firstLetterList = positionList.filter(p => p.isLastLetter && p.pageNo === positionNo)
|
||||
for (let j = 0; j < firstLetterList.length; j++) {
|
||||
const { index, pageNo, coordinate: { leftTop, leftBottom } } = firstLetterList[j]
|
||||
const lastLetterList = positionList.filter(p => p.isLastLetter && p.pageNo === positionNo)
|
||||
for (let j = 0; j < lastLetterList.length; j++) {
|
||||
const { index, pageNo, coordinate: { leftTop, leftBottom } } = lastLetterList[j]
|
||||
if (positionNo !== pageNo) continue
|
||||
if (y > leftTop[1] && y <= leftBottom[1]) {
|
||||
const isHead = x < this.options.margins[3]
|
||||
// 是否在头部
|
||||
if (isHead) {
|
||||
const headIndex = positionList.findIndex(p => p.pageNo === positionNo && p.rowNo === firstLetterList[j].rowNo)
|
||||
const headIndex = positionList.findIndex(p => p.pageNo === positionNo && p.rowNo === lastLetterList[j].rowNo)
|
||||
curPositionIndex = ~headIndex ? headIndex - 1 : index
|
||||
} else {
|
||||
curPositionIndex = index
|
||||
@@ -324,18 +332,30 @@ export class Position {
|
||||
}
|
||||
}
|
||||
if (!isLastArea) {
|
||||
// 判断所属位置是否属于header区域,当前位置小于第一行的上边距
|
||||
if (zoneManager.isMainActive()) {
|
||||
if (y < firstLetterList[0].coordinate.leftTop[1]) {
|
||||
const mainLastLetterList = isMainActive
|
||||
? lastLetterList
|
||||
: this.getOriginalMainPositionList().filter(p => p.isLastLetter && p.pageNo === positionNo)
|
||||
const firstPosition = mainLastLetterList[0]
|
||||
const lastPosition = mainLastLetterList[mainLastLetterList.length - 1]
|
||||
// 判断所属位置是否属于页眉页脚区域
|
||||
if (isMainActive) {
|
||||
// 页眉:当前位置小于第一行的上边距
|
||||
if (y < firstPosition.coordinate.leftTop[1]) {
|
||||
return {
|
||||
index: -1,
|
||||
zone: EditorZone.HEADER
|
||||
}
|
||||
}
|
||||
}
|
||||
// 判断所属位置是否属于main区域,当前位置大于第一行的上边距
|
||||
if (zoneManager.isHeaderActive()) {
|
||||
if (y > firstLetterList[0].coordinate.leftTop[1]) {
|
||||
// 页脚:当前位置大于最后一行的下边距
|
||||
if (y > lastPosition.coordinate.leftBottom[1]) {
|
||||
return {
|
||||
index: -1,
|
||||
zone: EditorZone.FOOTER
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// main区域:当前位置大于第一行的上边距 && 小于最后一行的下边距
|
||||
if (y >= firstPosition.coordinate.leftTop[1] && y <= lastPosition.coordinate.leftBottom[1]) {
|
||||
return {
|
||||
index: -1,
|
||||
zone: EditorZone.MAIN
|
||||
@@ -344,7 +364,7 @@ export class Position {
|
||||
}
|
||||
// 当前页最后一行
|
||||
return {
|
||||
index: firstLetterList[firstLetterList.length - 1]?.index || positionList.length - 1,
|
||||
index: lastLetterList[lastLetterList.length - 1]?.index || positionList.length - 1,
|
||||
}
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { I18n } from '../i18n/I18n'
|
||||
|
||||
export class Zone {
|
||||
|
||||
private readonly INDICATOR_PADDING = 2
|
||||
private readonly INDICATOR_TITLE_TRANSLATE = [20, 5]
|
||||
|
||||
private draw: Draw
|
||||
@@ -14,7 +15,7 @@ export class Zone {
|
||||
private container: HTMLDivElement
|
||||
|
||||
private currentZone: EditorZone
|
||||
private headerIndicatorContainer: HTMLDivElement | null
|
||||
private indicatorContainer: HTMLDivElement | null
|
||||
|
||||
constructor(draw: Draw) {
|
||||
this.draw = draw
|
||||
@@ -22,7 +23,7 @@ export class Zone {
|
||||
this.options = draw.getOptions()
|
||||
this.container = draw.getContainer()
|
||||
this.currentZone = EditorZone.MAIN
|
||||
this.headerIndicatorContainer = null
|
||||
this.indicatorContainer = null
|
||||
}
|
||||
|
||||
public isHeaderActive(): boolean {
|
||||
@@ -33,6 +34,10 @@ export class Zone {
|
||||
return this.getZone() === EditorZone.MAIN
|
||||
}
|
||||
|
||||
public isFooterActive(): boolean {
|
||||
return this.getZone() === EditorZone.FOOTER
|
||||
}
|
||||
|
||||
public getZone(): EditorZone {
|
||||
return this.currentZone
|
||||
}
|
||||
@@ -46,74 +51,87 @@ export class Zone {
|
||||
isSetCursor: false,
|
||||
isCompute: false
|
||||
})
|
||||
// 页眉指示器
|
||||
if (this.isHeaderActive()) {
|
||||
this.drawHeaderZoneIndicator()
|
||||
} else {
|
||||
this._clearHeaderZoneIndicator()
|
||||
}
|
||||
// 指示器
|
||||
this.drawZoneIndicator()
|
||||
}
|
||||
|
||||
public drawHeaderZoneIndicator() {
|
||||
this._clearHeaderZoneIndicator()
|
||||
public drawZoneIndicator() {
|
||||
this._clearZoneIndicator()
|
||||
if (!this.isHeaderActive() && !this.isFooterActive()) return
|
||||
const { scale } = this.options
|
||||
const isHeaderActive = this.isHeaderActive()
|
||||
const [offsetX, offsetY] = this.INDICATOR_TITLE_TRANSLATE
|
||||
this.headerIndicatorContainer = document.createElement('div')
|
||||
this.headerIndicatorContainer.classList.add(`${EDITOR_PREFIX}-header-indicator`)
|
||||
const pageList = this.draw.getPageList()
|
||||
const margins = this.draw.getMargins()
|
||||
const innerWidth = this.draw.getInnerWidth()
|
||||
const pageHeight = this.draw.getHeight()
|
||||
const pageGap = this.draw.getPageGap()
|
||||
const preY = pageHeight + pageGap
|
||||
// 创建指示器容器
|
||||
this.indicatorContainer = document.createElement('div')
|
||||
this.indicatorContainer.classList.add(`${EDITOR_PREFIX}-zone-indicator`)
|
||||
// 指示器位置
|
||||
const header = this.draw.getHeader()
|
||||
const headerHeight = header.getHeight()
|
||||
const headerTop = header.getHeaderTop()
|
||||
|
||||
const footer = this.draw.getFooter()
|
||||
const indicatorHeight = isHeaderActive
|
||||
? header.getHeight()
|
||||
: footer.getHeight()
|
||||
const indicatorTop = isHeaderActive
|
||||
? header.getHeaderTop()
|
||||
: pageHeight - footer.getFooterBottom() - indicatorHeight
|
||||
for (let p = 0; p < pageList.length; p++) {
|
||||
const startY = preY * p + headerTop
|
||||
const indicatorTitle = document.createElement('div')
|
||||
const startY = preY * p + indicatorTop
|
||||
const indicatorLeftX = margins[3] - this.INDICATOR_PADDING
|
||||
const indicatorRightX = margins[3] + innerWidth + this.INDICATOR_PADDING
|
||||
const indicatorTopY = isHeaderActive
|
||||
? startY - this.INDICATOR_PADDING
|
||||
: startY + indicatorHeight + this.INDICATOR_PADDING
|
||||
const indicatorBottomY = isHeaderActive
|
||||
? startY + indicatorHeight + this.INDICATOR_PADDING
|
||||
: startY - this.INDICATOR_PADDING
|
||||
// 标题
|
||||
indicatorTitle.innerText = this.i18n.t('frame.header')
|
||||
indicatorTitle.style.top = `${startY + headerHeight}px`
|
||||
const indicatorTitle = document.createElement('div')
|
||||
indicatorTitle.innerText = this.i18n.t(`frame.${isHeaderActive ? 'header' : 'footer'}`)
|
||||
indicatorTitle.style.top = `${indicatorBottomY}px`
|
||||
indicatorTitle.style.transform = `translate(${offsetX * scale}px, ${offsetY * scale}px) scale(${scale})`
|
||||
this.headerIndicatorContainer.append(indicatorTitle)
|
||||
this.indicatorContainer.append(indicatorTitle)
|
||||
|
||||
// 上边线
|
||||
const lineTop = document.createElement('span')
|
||||
lineTop.classList.add(`${EDITOR_PREFIX}-header-indicator-border__top`)
|
||||
lineTop.style.top = `${startY}px`
|
||||
lineTop.classList.add(`${EDITOR_PREFIX}-zone-indicator-border__top`)
|
||||
lineTop.style.top = `${indicatorTopY}px`
|
||||
lineTop.style.width = `${innerWidth}px`
|
||||
lineTop.style.marginLeft = `${margins[3]}px`
|
||||
this.headerIndicatorContainer.append(lineTop)
|
||||
this.indicatorContainer.append(lineTop)
|
||||
|
||||
// 左边线
|
||||
const lineLeft = document.createElement('span')
|
||||
lineLeft.classList.add(`${EDITOR_PREFIX}-header-indicator-border__left`)
|
||||
lineLeft.classList.add(`${EDITOR_PREFIX}-zone-indicator-border__left`)
|
||||
lineLeft.style.top = `${startY}px`
|
||||
lineLeft.style.height = `${headerHeight}px`
|
||||
lineLeft.style.left = `${margins[3]}px`
|
||||
this.headerIndicatorContainer.append(lineLeft)
|
||||
lineLeft.style.height = `${indicatorHeight}px`
|
||||
lineLeft.style.left = `${indicatorLeftX}px`
|
||||
this.indicatorContainer.append(lineLeft)
|
||||
|
||||
// 下边线
|
||||
const lineBottom = document.createElement('span')
|
||||
lineBottom.classList.add(`${EDITOR_PREFIX}-header-indicator-border__bottom`)
|
||||
lineBottom.style.top = `${startY + headerHeight}px`
|
||||
this.headerIndicatorContainer.append(lineBottom)
|
||||
lineBottom.classList.add(`${EDITOR_PREFIX}-zone-indicator-border__bottom`)
|
||||
lineBottom.style.top = `${indicatorBottomY}px`
|
||||
this.indicatorContainer.append(lineBottom)
|
||||
|
||||
// 右边线
|
||||
const lineRight = document.createElement('span')
|
||||
lineRight.classList.add(`${EDITOR_PREFIX}-header-indicator-border__right`)
|
||||
lineRight.classList.add(`${EDITOR_PREFIX}-zone-indicator-border__right`)
|
||||
lineRight.style.top = `${startY}px`
|
||||
lineRight.style.height = `${headerHeight}px`
|
||||
lineRight.style.left = `${margins[3] + innerWidth}px`
|
||||
this.headerIndicatorContainer.append(lineRight)
|
||||
lineRight.style.height = `${indicatorHeight}px`
|
||||
lineRight.style.left = `${indicatorRightX}px`
|
||||
this.indicatorContainer.append(lineRight)
|
||||
}
|
||||
this.container.append(this.headerIndicatorContainer)
|
||||
this.container.append(this.indicatorContainer)
|
||||
}
|
||||
|
||||
private _clearHeaderZoneIndicator() {
|
||||
this.headerIndicatorContainer?.remove()
|
||||
this.headerIndicatorContainer = null
|
||||
private _clearZoneIndicator() {
|
||||
this.indicatorContainer?.remove()
|
||||
this.indicatorContainer = null
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,12 @@
|
||||
import { MaxHeightRatio } from '../enum/Common'
|
||||
|
||||
export const ZERO = '\u200B'
|
||||
export const WRAP = '\n'
|
||||
export const HORIZON_TAB = '\t'
|
||||
export const NBSP = '\u0020'
|
||||
export const NBSP = '\u0020'
|
||||
|
||||
export const maxHeightRadioMapping: Record<MaxHeightRatio, number> = {
|
||||
[MaxHeightRatio.HALF]: 1 / 2,
|
||||
[MaxHeightRatio.ONE_THIRD]: 1 / 3,
|
||||
[MaxHeightRatio.QUARTER]: 1 / 4
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { IFooter } from '../../interface/Footer'
|
||||
import { MaxHeightRatio } from '../enum/Common'
|
||||
|
||||
export const defaultFooterOption: Readonly<Required<IFooter>> = {
|
||||
bottom: 30,
|
||||
maxHeightRadio: MaxHeightRatio.HALF
|
||||
}
|
||||
@@ -1,13 +1,7 @@
|
||||
import { IHeader } from '../../interface/Header'
|
||||
import { HeaderMaxHeightRatio } from '../enum/Header'
|
||||
import { MaxHeightRatio } from '../enum/Common'
|
||||
|
||||
export const defaultHeaderOption: Readonly<Required<IHeader>> = {
|
||||
top: 30,
|
||||
maxHeightRadio: HeaderMaxHeightRatio.HALF
|
||||
}
|
||||
|
||||
export const maxHeightRadioMapping: Record<HeaderMaxHeightRatio, number> = {
|
||||
[HeaderMaxHeightRatio.HALF]: 1 / 2,
|
||||
[HeaderMaxHeightRatio.ONE_THIRD]: 1 / 3,
|
||||
[HeaderMaxHeightRatio.QUARTER]: 1 / 4
|
||||
maxHeightRadio: MaxHeightRatio.HALF
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
export enum HeaderMaxHeightRatio {
|
||||
export enum MaxHeightRatio {
|
||||
HALF = 'half',
|
||||
ONE_THIRD = 'one-third',
|
||||
QUARTER = 'quarter'
|
||||
+20
-8
@@ -35,6 +35,9 @@ import { IPageNumber } from './interface/PageNumber'
|
||||
import { defaultPageNumberOption } from './dataset/constant/PageNumber'
|
||||
import { VerticalAlign } from './dataset/enum/VerticalAlign'
|
||||
import { TableBorder } from './dataset/enum/table/Table'
|
||||
import { IFooter } from './interface/Footer'
|
||||
import { defaultFooterOption } from './dataset/constant/Footer'
|
||||
import { MaxHeightRatio } from './dataset/enum/Common'
|
||||
|
||||
export default class Editor {
|
||||
|
||||
@@ -48,6 +51,10 @@ export default class Editor {
|
||||
...defaultHeaderOption,
|
||||
...options.header
|
||||
}
|
||||
const footerOptions: Required<IFooter> = {
|
||||
...defaultFooterOption,
|
||||
...options.footer
|
||||
}
|
||||
const pageNumberOptions: Required<IPageNumber> = {
|
||||
...defaultPageNumberOption,
|
||||
...options.pageNumber
|
||||
@@ -105,6 +112,7 @@ export default class Editor {
|
||||
inactiveAlpha: 0.6,
|
||||
...options,
|
||||
header: headerOptions,
|
||||
footer: footerOptions,
|
||||
pageNumber: pageNumberOptions,
|
||||
watermark: waterMarkOptions,
|
||||
control: controlOptions,
|
||||
@@ -114,18 +122,20 @@ export default class Editor {
|
||||
// 数据处理
|
||||
let headerElementList: IElement[] = []
|
||||
let mainElementList: IElement[] = []
|
||||
let footerElementList: IElement[] = []
|
||||
if (Array.isArray(data)) {
|
||||
mainElementList = data
|
||||
} else {
|
||||
headerElementList = data.header || []
|
||||
mainElementList = data.main
|
||||
footerElementList = data.footer || []
|
||||
}
|
||||
formatElementList(headerElementList, {
|
||||
editorOptions
|
||||
})
|
||||
formatElementList(mainElementList, {
|
||||
editorOptions
|
||||
})
|
||||
[headerElementList, mainElementList, footerElementList]
|
||||
.forEach(elementList => {
|
||||
formatElementList(elementList, {
|
||||
editorOptions
|
||||
})
|
||||
})
|
||||
// 监听
|
||||
this.listener = new Listener()
|
||||
// 启动
|
||||
@@ -134,7 +144,8 @@ export default class Editor {
|
||||
editorOptions,
|
||||
{
|
||||
header: headerElementList,
|
||||
main: mainElementList
|
||||
main: mainElementList,
|
||||
footer: footerElementList
|
||||
},
|
||||
this.listener
|
||||
)
|
||||
@@ -177,7 +188,8 @@ export {
|
||||
KeyMap,
|
||||
BlockType,
|
||||
PaperDirection,
|
||||
TableBorder
|
||||
TableBorder,
|
||||
MaxHeightRatio
|
||||
}
|
||||
|
||||
// 对外类型
|
||||
|
||||
@@ -3,6 +3,7 @@ import { EditorMode, PageMode, PaperDirection } from '../dataset/enum/Editor'
|
||||
import { ICheckboxOption } from './Checkbox'
|
||||
import { IControlOption } from './Control'
|
||||
import { ICursorOption } from './Cursor'
|
||||
import { IFooter } from './Footer'
|
||||
import { IHeader } from './Header'
|
||||
import { IMargin } from './Margin'
|
||||
import { IPageNumber } from './PageNumber'
|
||||
@@ -49,6 +50,7 @@ export interface IEditorOption {
|
||||
paperDirection?: PaperDirection;
|
||||
inactiveAlpha?: number;
|
||||
header?: IHeader;
|
||||
footer?: IFooter;
|
||||
pageNumber?: IPageNumber;
|
||||
watermark?: IWatermark;
|
||||
control?: IControlOption;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { MaxHeightRatio } from '../dataset/enum/Common'
|
||||
|
||||
export interface IFooter {
|
||||
bottom?: number;
|
||||
maxHeightRadio?: MaxHeightRatio;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { HeaderMaxHeightRatio } from '../dataset/enum/Header'
|
||||
import { MaxHeightRatio } from '../dataset/enum/Common'
|
||||
|
||||
export interface IHeader {
|
||||
top?: number;
|
||||
maxHeightRadio?: HeaderMaxHeightRatio;
|
||||
maxHeightRadio?: MaxHeightRatio;
|
||||
}
|
||||
Reference in New Issue
Block a user