feat: draw line break marker #520
This commit is contained in:
@@ -69,6 +69,7 @@ interface IEditorOption {
|
|||||||
pageBreak?: IPageBreak // PageBreak option。{font?:string; fontSize?:number; lineDash?:number[];}
|
pageBreak?: IPageBreak // PageBreak option。{font?:string; fontSize?:number; lineDash?:number[];}
|
||||||
zone?: IZoneOption // Zone option。{tipDisabled?:boolean;}
|
zone?: IZoneOption // Zone option。{tipDisabled?:boolean;}
|
||||||
background?: IBackgroundOption // Background option. {color?:string; image?:string; size?:BackgroundSize; repeat?:BackgroundRepeat;}。default: {color: '#FFFFFF'}
|
background?: IBackgroundOption // Background option. {color?:string; image?:string; size?:BackgroundSize; repeat?:BackgroundRepeat;}。default: {color: '#FFFFFF'}
|
||||||
|
lineBreak?: ILineBreakOption // LineBreak option. {disabled?:boolean; color?:string; lineWidth?:number;}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ interface IEditorOption {
|
|||||||
pageBreak?: IPageBreak // 分页符配置。{font?:string; fontSize?:number; lineDash?:number[];}
|
pageBreak?: IPageBreak // 分页符配置。{font?:string; fontSize?:number; lineDash?:number[];}
|
||||||
zone?: IZoneOption // 编辑器区域配置。{tipDisabled?:boolean;}
|
zone?: IZoneOption // 编辑器区域配置。{tipDisabled?:boolean;}
|
||||||
background?: IBackgroundOption // 背景配置。{color?:string; image?:string; size?:BackgroundSize; repeat?:BackgroundRepeat;}。默认:{color: '#FFFFFF'}
|
background?: IBackgroundOption // 背景配置。{color?:string; image?:string; size?:BackgroundSize; repeat?:BackgroundRepeat;}。默认:{color: '#FFFFFF'}
|
||||||
|
lineBreak?: ILineBreakOption // 换行符配置。{disabled?:boolean; color?:string; lineWidth?:number;}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ import { Group } from './interactive/Group'
|
|||||||
import { Override } from '../override/Override'
|
import { Override } from '../override/Override'
|
||||||
import { ImageDisplay } from '../../dataset/enum/Common'
|
import { ImageDisplay } from '../../dataset/enum/Common'
|
||||||
import { PUNCTUATION_REG } from '../../dataset/constant/Regular'
|
import { PUNCTUATION_REG } from '../../dataset/constant/Regular'
|
||||||
|
import { LineBreakParticle } from './particle/LineBreakParticle'
|
||||||
|
|
||||||
export class Draw {
|
export class Draw {
|
||||||
private container: HTMLDivElement
|
private container: HTMLDivElement
|
||||||
@@ -143,6 +144,7 @@ export class Draw {
|
|||||||
private checkboxParticle: CheckboxParticle
|
private checkboxParticle: CheckboxParticle
|
||||||
private blockParticle: BlockParticle
|
private blockParticle: BlockParticle
|
||||||
private listParticle: ListParticle
|
private listParticle: ListParticle
|
||||||
|
private lineBreakParticle: LineBreakParticle
|
||||||
private control: Control
|
private control: Control
|
||||||
private workerManager: WorkerManager
|
private workerManager: WorkerManager
|
||||||
private scrollObserver: ScrollObserver
|
private scrollObserver: ScrollObserver
|
||||||
@@ -216,6 +218,7 @@ export class Draw {
|
|||||||
this.checkboxParticle = new CheckboxParticle(this)
|
this.checkboxParticle = new CheckboxParticle(this)
|
||||||
this.blockParticle = new BlockParticle(this)
|
this.blockParticle = new BlockParticle(this)
|
||||||
this.listParticle = new ListParticle(this)
|
this.listParticle = new ListParticle(this)
|
||||||
|
this.lineBreakParticle = new LineBreakParticle(this)
|
||||||
this.control = new Control(this)
|
this.control = new Control(this)
|
||||||
|
|
||||||
this.scrollObserver = new ScrollObserver(this)
|
this.scrollObserver = new ScrollObserver(this)
|
||||||
@@ -510,6 +513,10 @@ export class Draw {
|
|||||||
return this.range
|
return this.range
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getLineBreakParticle(): LineBreakParticle {
|
||||||
|
return this.lineBreakParticle
|
||||||
|
}
|
||||||
|
|
||||||
public getHeaderElementList(): IElement[] {
|
public getHeaderElementList(): IElement[] {
|
||||||
return this.header.getElementList()
|
return this.header.getElementList()
|
||||||
}
|
}
|
||||||
@@ -1531,7 +1538,8 @@ export class Draw {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
listId = element.listId
|
listId = element.listId
|
||||||
if (
|
// 是否强制换行
|
||||||
|
const isForceBreak =
|
||||||
element.type === ElementType.SEPARATOR ||
|
element.type === ElementType.SEPARATOR ||
|
||||||
element.type === ElementType.TABLE ||
|
element.type === ElementType.TABLE ||
|
||||||
preElement?.type === ElementType.TABLE ||
|
preElement?.type === ElementType.TABLE ||
|
||||||
@@ -1539,10 +1547,13 @@ export class Draw {
|
|||||||
element.type === ElementType.BLOCK ||
|
element.type === ElementType.BLOCK ||
|
||||||
preElement?.imgDisplay === ImageDisplay.INLINE ||
|
preElement?.imgDisplay === ImageDisplay.INLINE ||
|
||||||
element.imgDisplay === ImageDisplay.INLINE ||
|
element.imgDisplay === ImageDisplay.INLINE ||
|
||||||
curRowWidth > availableWidth ||
|
preElement?.listId !== element.listId ||
|
||||||
(i !== 0 && element.value === ZERO) ||
|
(i !== 0 && element.value === ZERO)
|
||||||
preElement?.listId !== element.listId
|
// 是否宽度不足导致换行
|
||||||
) {
|
const isWidthNotEnough = curRowWidth > availableWidth
|
||||||
|
if (isForceBreak || isWidthNotEnough) {
|
||||||
|
// 换行原因:宽度不足
|
||||||
|
curRow.isWidthNotEnough = isWidthNotEnough && !isForceBreak
|
||||||
// 减小行元素前第一行空行行高
|
// 减小行元素前第一行空行行高
|
||||||
if (
|
if (
|
||||||
curRow.startIndex === 0 &&
|
curRow.startIndex === 0 &&
|
||||||
@@ -1702,10 +1713,17 @@ export class Draw {
|
|||||||
// 优先绘制高亮元素
|
// 优先绘制高亮元素
|
||||||
this._drawHighlight(ctx, payload)
|
this._drawHighlight(ctx, payload)
|
||||||
// 绘制元素、下划线、删除线、选区
|
// 绘制元素、下划线、删除线、选区
|
||||||
const { rowList, pageNo, elementList, positionList, startIndex, zone } =
|
const { scale, tdPadding, group, lineBreak } = this.options
|
||||||
payload
|
const {
|
||||||
|
rowList,
|
||||||
|
pageNo,
|
||||||
|
elementList,
|
||||||
|
positionList,
|
||||||
|
startIndex,
|
||||||
|
zone,
|
||||||
|
isDrawLineBreak = !lineBreak.disabled
|
||||||
|
} = payload
|
||||||
const isPrintMode = this.mode === EditorMode.PRINT
|
const isPrintMode = this.mode === EditorMode.PRINT
|
||||||
const { scale, tdPadding, group } = this.options
|
|
||||||
const { isCrossRowCol, tableId } = this.range.getRange()
|
const { isCrossRowCol, tableId } = this.range.getRange()
|
||||||
let index = startIndex
|
let index = startIndex
|
||||||
for (let i = 0; i < rowList.length; i++) {
|
for (let i = 0; i < rowList.length; i++) {
|
||||||
@@ -1807,6 +1825,14 @@ export class Draw {
|
|||||||
this.textParticle.complete()
|
this.textParticle.complete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 换行符绘制
|
||||||
|
if (
|
||||||
|
isDrawLineBreak &&
|
||||||
|
!curRow.isWidthNotEnough &&
|
||||||
|
j === curRow.elementList.length - 1
|
||||||
|
) {
|
||||||
|
this.lineBreakParticle.render(ctx, element, x, y + curRow.height / 2)
|
||||||
|
}
|
||||||
// 边框绘制(目前仅支持控件)
|
// 边框绘制(目前仅支持控件)
|
||||||
if (element.control?.border) {
|
if (element.control?.border) {
|
||||||
// 不同控件边框立刻绘制
|
// 不同控件边框立刻绘制
|
||||||
@@ -1961,7 +1987,8 @@ export class Draw {
|
|||||||
pageNo,
|
pageNo,
|
||||||
startIndex: 0,
|
startIndex: 0,
|
||||||
innerWidth: (td.width! - tdPaddingWidth) * scale,
|
innerWidth: (td.width! - tdPaddingWidth) * scale,
|
||||||
zone
|
zone,
|
||||||
|
isDrawLineBreak
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { IRow } from '../../../interface/Row'
|
|||||||
import { formatElementList } from '../../../utils/element'
|
import { formatElementList } from '../../../utils/element'
|
||||||
import { Position } from '../../position/Position'
|
import { Position } from '../../position/Position'
|
||||||
import { Draw } from '../Draw'
|
import { Draw } from '../Draw'
|
||||||
|
import { LineBreakParticle } from '../particle/LineBreakParticle'
|
||||||
|
|
||||||
export class Placeholder {
|
export class Placeholder {
|
||||||
private draw: Draw
|
private draw: Draw
|
||||||
@@ -45,10 +46,15 @@ export class Placeholder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _computePositionList() {
|
private _computePositionList() {
|
||||||
|
const { lineBreak, scale } = this.options
|
||||||
const headerExtraHeight = this.draw.getHeader().getExtraHeight()
|
const headerExtraHeight = this.draw.getHeader().getExtraHeight()
|
||||||
const innerWidth = this.draw.getInnerWidth()
|
const innerWidth = this.draw.getInnerWidth()
|
||||||
const margins = this.draw.getMargins()
|
const margins = this.draw.getMargins()
|
||||||
const startX = margins[3]
|
let startX = margins[3]
|
||||||
|
// 换行符绘制开启时,移动起始位置
|
||||||
|
if (!lineBreak.disabled) {
|
||||||
|
startX += (LineBreakParticle.WIDTH + LineBreakParticle.GAP) * scale
|
||||||
|
}
|
||||||
const startY = margins[0] + headerExtraHeight
|
const startY = margins[0] + headerExtraHeight
|
||||||
this.position.computePageRowPosition({
|
this.position.computePageRowPosition({
|
||||||
positionList: this.positionList,
|
positionList: this.positionList,
|
||||||
@@ -92,7 +98,8 @@ export class Placeholder {
|
|||||||
rowList: this.rowList,
|
rowList: this.rowList,
|
||||||
pageNo: 0,
|
pageNo: 0,
|
||||||
startIndex: 0,
|
startIndex: 0,
|
||||||
innerWidth
|
innerWidth,
|
||||||
|
isDrawLineBreak: false
|
||||||
})
|
})
|
||||||
ctx.restore()
|
ctx.restore()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { DeepRequired } from '../../../interface/Common'
|
||||||
|
import { IEditorOption } from '../../../interface/Editor'
|
||||||
|
import { IRowElement } from '../../../interface/Row'
|
||||||
|
import { Draw } from '../Draw'
|
||||||
|
|
||||||
|
export class LineBreakParticle {
|
||||||
|
private options: DeepRequired<IEditorOption>
|
||||||
|
public static readonly WIDTH = 12
|
||||||
|
public static readonly HEIGHT = 9
|
||||||
|
public static readonly GAP = 3 // 距离左边间隙
|
||||||
|
|
||||||
|
constructor(draw: Draw) {
|
||||||
|
this.options = draw.getOptions()
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
element: IRowElement,
|
||||||
|
x: number,
|
||||||
|
y: number
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
scale,
|
||||||
|
lineBreak: { color, lineWidth }
|
||||||
|
} = this.options
|
||||||
|
ctx.save()
|
||||||
|
ctx.beginPath()
|
||||||
|
// 换行符尺寸设置为9像素
|
||||||
|
const top = y - (LineBreakParticle.HEIGHT * scale) / 2
|
||||||
|
const left = x + element.metrics.width
|
||||||
|
// 移动位置并设置缩放
|
||||||
|
ctx.translate(left, top)
|
||||||
|
ctx.scale(scale, scale)
|
||||||
|
// 样式设置
|
||||||
|
ctx.strokeStyle = color
|
||||||
|
ctx.lineWidth = lineWidth
|
||||||
|
ctx.lineCap = 'round'
|
||||||
|
ctx.lineJoin = 'round'
|
||||||
|
ctx.beginPath()
|
||||||
|
// 回车折线
|
||||||
|
ctx.moveTo(8, 0)
|
||||||
|
ctx.lineTo(12, 0)
|
||||||
|
ctx.lineTo(12, 6)
|
||||||
|
ctx.lineTo(3, 6)
|
||||||
|
// 箭头向上
|
||||||
|
ctx.moveTo(3, 6)
|
||||||
|
ctx.lineTo(6, 3)
|
||||||
|
// 箭头向下
|
||||||
|
ctx.moveTo(3, 6)
|
||||||
|
ctx.lineTo(6, 9)
|
||||||
|
ctx.stroke()
|
||||||
|
ctx.closePath()
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { ILineBreakOption } from '../../interface/LineBreak'
|
||||||
|
|
||||||
|
export const defaultLineBreak: Readonly<Required<ILineBreakOption>> = {
|
||||||
|
disabled: true,
|
||||||
|
color: '#CCCCCC',
|
||||||
|
lineWidth: 1.5
|
||||||
|
}
|
||||||
+8
-1
@@ -76,6 +76,8 @@ import { IBackgroundOption } from './interface/Background'
|
|||||||
import { defaultBackground } from './dataset/constant/Background'
|
import { defaultBackground } from './dataset/constant/Background'
|
||||||
import { BackgroundRepeat, BackgroundSize } from './dataset/enum/Background'
|
import { BackgroundRepeat, BackgroundSize } from './dataset/enum/Background'
|
||||||
import { TextDecorationStyle } from './dataset/enum/Text'
|
import { TextDecorationStyle } from './dataset/enum/Text'
|
||||||
|
import { ILineBreakOption } from './interface/LineBreak'
|
||||||
|
import { defaultLineBreak } from './dataset/constant/LineBreak'
|
||||||
|
|
||||||
export default class Editor {
|
export default class Editor {
|
||||||
public command: Command
|
public command: Command
|
||||||
@@ -143,6 +145,10 @@ export default class Editor {
|
|||||||
...defaultBackground,
|
...defaultBackground,
|
||||||
...options.background
|
...options.background
|
||||||
}
|
}
|
||||||
|
const lineBreakOptions: Required<ILineBreakOption> = {
|
||||||
|
...defaultLineBreak,
|
||||||
|
...options.lineBreak
|
||||||
|
}
|
||||||
|
|
||||||
const editorOptions: DeepRequired<IEditorOption> = {
|
const editorOptions: DeepRequired<IEditorOption> = {
|
||||||
mode: EditorMode.EDIT,
|
mode: EditorMode.EDIT,
|
||||||
@@ -200,7 +206,8 @@ export default class Editor {
|
|||||||
group: groupOptions,
|
group: groupOptions,
|
||||||
pageBreak: pageBreakOptions,
|
pageBreak: pageBreakOptions,
|
||||||
zone: zoneOptions,
|
zone: zoneOptions,
|
||||||
background: backgroundOptions
|
background: backgroundOptions,
|
||||||
|
lineBreak: lineBreakOptions
|
||||||
}
|
}
|
||||||
// 数据处理
|
// 数据处理
|
||||||
data = deepClone(data)
|
data = deepClone(data)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export interface IDrawRowPayload {
|
|||||||
startIndex: number
|
startIndex: number
|
||||||
innerWidth: number
|
innerWidth: number
|
||||||
zone?: EditorZone
|
zone?: EditorZone
|
||||||
|
isDrawLineBreak?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IDrawFloatPayload {
|
export interface IDrawFloatPayload {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { ICursorOption } from './Cursor'
|
|||||||
import { IFooter } from './Footer'
|
import { IFooter } from './Footer'
|
||||||
import { IGroup } from './Group'
|
import { IGroup } from './Group'
|
||||||
import { IHeader } from './Header'
|
import { IHeader } from './Header'
|
||||||
|
import { ILineBreakOption } from './LineBreak'
|
||||||
import { IMargin } from './Margin'
|
import { IMargin } from './Margin'
|
||||||
import { IPageBreak } from './PageBreak'
|
import { IPageBreak } from './PageBreak'
|
||||||
import { IPageNumber } from './PageNumber'
|
import { IPageNumber } from './PageNumber'
|
||||||
@@ -83,6 +84,7 @@ export interface IEditorOption {
|
|||||||
pageBreak?: IPageBreak
|
pageBreak?: IPageBreak
|
||||||
zone?: IZoneOption
|
zone?: IZoneOption
|
||||||
background?: IBackgroundOption
|
background?: IBackgroundOption
|
||||||
|
lineBreak?: ILineBreakOption
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEditorResult {
|
export interface IEditorResult {
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export interface ILineBreakOption {
|
||||||
|
disabled?: boolean
|
||||||
|
color?: string
|
||||||
|
lineWidth?: number
|
||||||
|
}
|
||||||
@@ -18,4 +18,5 @@ export interface IRow {
|
|||||||
listIndex?: number
|
listIndex?: number
|
||||||
offsetX?: number
|
offsetX?: number
|
||||||
elementList: IRowElement[]
|
elementList: IRowElement[]
|
||||||
|
isWidthNotEnough?: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user