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[];}
|
||||
zone?: IZoneOption // Zone option。{tipDisabled?:boolean;}
|
||||
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[];}
|
||||
zone?: IZoneOption // 编辑器区域配置。{tipDisabled?:boolean;}
|
||||
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 { ImageDisplay } from '../../dataset/enum/Common'
|
||||
import { PUNCTUATION_REG } from '../../dataset/constant/Regular'
|
||||
import { LineBreakParticle } from './particle/LineBreakParticle'
|
||||
|
||||
export class Draw {
|
||||
private container: HTMLDivElement
|
||||
@@ -143,6 +144,7 @@ export class Draw {
|
||||
private checkboxParticle: CheckboxParticle
|
||||
private blockParticle: BlockParticle
|
||||
private listParticle: ListParticle
|
||||
private lineBreakParticle: LineBreakParticle
|
||||
private control: Control
|
||||
private workerManager: WorkerManager
|
||||
private scrollObserver: ScrollObserver
|
||||
@@ -216,6 +218,7 @@ export class Draw {
|
||||
this.checkboxParticle = new CheckboxParticle(this)
|
||||
this.blockParticle = new BlockParticle(this)
|
||||
this.listParticle = new ListParticle(this)
|
||||
this.lineBreakParticle = new LineBreakParticle(this)
|
||||
this.control = new Control(this)
|
||||
|
||||
this.scrollObserver = new ScrollObserver(this)
|
||||
@@ -510,6 +513,10 @@ export class Draw {
|
||||
return this.range
|
||||
}
|
||||
|
||||
public getLineBreakParticle(): LineBreakParticle {
|
||||
return this.lineBreakParticle
|
||||
}
|
||||
|
||||
public getHeaderElementList(): IElement[] {
|
||||
return this.header.getElementList()
|
||||
}
|
||||
@@ -1531,7 +1538,8 @@ export class Draw {
|
||||
}
|
||||
}
|
||||
listId = element.listId
|
||||
if (
|
||||
// 是否强制换行
|
||||
const isForceBreak =
|
||||
element.type === ElementType.SEPARATOR ||
|
||||
element.type === ElementType.TABLE ||
|
||||
preElement?.type === ElementType.TABLE ||
|
||||
@@ -1539,10 +1547,13 @@ export class Draw {
|
||||
element.type === ElementType.BLOCK ||
|
||||
preElement?.imgDisplay === ImageDisplay.INLINE ||
|
||||
element.imgDisplay === ImageDisplay.INLINE ||
|
||||
curRowWidth > availableWidth ||
|
||||
(i !== 0 && element.value === ZERO) ||
|
||||
preElement?.listId !== element.listId
|
||||
) {
|
||||
preElement?.listId !== element.listId ||
|
||||
(i !== 0 && element.value === ZERO)
|
||||
// 是否宽度不足导致换行
|
||||
const isWidthNotEnough = curRowWidth > availableWidth
|
||||
if (isForceBreak || isWidthNotEnough) {
|
||||
// 换行原因:宽度不足
|
||||
curRow.isWidthNotEnough = isWidthNotEnough && !isForceBreak
|
||||
// 减小行元素前第一行空行行高
|
||||
if (
|
||||
curRow.startIndex === 0 &&
|
||||
@@ -1702,10 +1713,17 @@ export class Draw {
|
||||
// 优先绘制高亮元素
|
||||
this._drawHighlight(ctx, payload)
|
||||
// 绘制元素、下划线、删除线、选区
|
||||
const { rowList, pageNo, elementList, positionList, startIndex, zone } =
|
||||
payload
|
||||
const { scale, tdPadding, group, lineBreak } = this.options
|
||||
const {
|
||||
rowList,
|
||||
pageNo,
|
||||
elementList,
|
||||
positionList,
|
||||
startIndex,
|
||||
zone,
|
||||
isDrawLineBreak = !lineBreak.disabled
|
||||
} = payload
|
||||
const isPrintMode = this.mode === EditorMode.PRINT
|
||||
const { scale, tdPadding, group } = this.options
|
||||
const { isCrossRowCol, tableId } = this.range.getRange()
|
||||
let index = startIndex
|
||||
for (let i = 0; i < rowList.length; i++) {
|
||||
@@ -1807,6 +1825,14 @@ export class Draw {
|
||||
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) {
|
||||
// 不同控件边框立刻绘制
|
||||
@@ -1961,7 +1987,8 @@ export class Draw {
|
||||
pageNo,
|
||||
startIndex: 0,
|
||||
innerWidth: (td.width! - tdPaddingWidth) * scale,
|
||||
zone
|
||||
zone,
|
||||
isDrawLineBreak
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { IRow } from '../../../interface/Row'
|
||||
import { formatElementList } from '../../../utils/element'
|
||||
import { Position } from '../../position/Position'
|
||||
import { Draw } from '../Draw'
|
||||
import { LineBreakParticle } from '../particle/LineBreakParticle'
|
||||
|
||||
export class Placeholder {
|
||||
private draw: Draw
|
||||
@@ -45,10 +46,15 @@ export class Placeholder {
|
||||
}
|
||||
|
||||
private _computePositionList() {
|
||||
const { lineBreak, scale } = this.options
|
||||
const headerExtraHeight = this.draw.getHeader().getExtraHeight()
|
||||
const innerWidth = this.draw.getInnerWidth()
|
||||
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
|
||||
this.position.computePageRowPosition({
|
||||
positionList: this.positionList,
|
||||
@@ -92,7 +98,8 @@ export class Placeholder {
|
||||
rowList: this.rowList,
|
||||
pageNo: 0,
|
||||
startIndex: 0,
|
||||
innerWidth
|
||||
innerWidth,
|
||||
isDrawLineBreak: false
|
||||
})
|
||||
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 { BackgroundRepeat, BackgroundSize } from './dataset/enum/Background'
|
||||
import { TextDecorationStyle } from './dataset/enum/Text'
|
||||
import { ILineBreakOption } from './interface/LineBreak'
|
||||
import { defaultLineBreak } from './dataset/constant/LineBreak'
|
||||
|
||||
export default class Editor {
|
||||
public command: Command
|
||||
@@ -143,6 +145,10 @@ export default class Editor {
|
||||
...defaultBackground,
|
||||
...options.background
|
||||
}
|
||||
const lineBreakOptions: Required<ILineBreakOption> = {
|
||||
...defaultLineBreak,
|
||||
...options.lineBreak
|
||||
}
|
||||
|
||||
const editorOptions: DeepRequired<IEditorOption> = {
|
||||
mode: EditorMode.EDIT,
|
||||
@@ -200,7 +206,8 @@ export default class Editor {
|
||||
group: groupOptions,
|
||||
pageBreak: pageBreakOptions,
|
||||
zone: zoneOptions,
|
||||
background: backgroundOptions
|
||||
background: backgroundOptions,
|
||||
lineBreak: lineBreakOptions
|
||||
}
|
||||
// 数据处理
|
||||
data = deepClone(data)
|
||||
|
||||
@@ -32,6 +32,7 @@ export interface IDrawRowPayload {
|
||||
startIndex: number
|
||||
innerWidth: number
|
||||
zone?: EditorZone
|
||||
isDrawLineBreak?: boolean
|
||||
}
|
||||
|
||||
export interface IDrawFloatPayload {
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ICursorOption } from './Cursor'
|
||||
import { IFooter } from './Footer'
|
||||
import { IGroup } from './Group'
|
||||
import { IHeader } from './Header'
|
||||
import { ILineBreakOption } from './LineBreak'
|
||||
import { IMargin } from './Margin'
|
||||
import { IPageBreak } from './PageBreak'
|
||||
import { IPageNumber } from './PageNumber'
|
||||
@@ -83,6 +84,7 @@ export interface IEditorOption {
|
||||
pageBreak?: IPageBreak
|
||||
zone?: IZoneOption
|
||||
background?: IBackgroundOption
|
||||
lineBreak?: ILineBreakOption
|
||||
}
|
||||
|
||||
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
|
||||
offsetX?: number
|
||||
elementList: IRowElement[]
|
||||
isWidthNotEnough?: boolean
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user