From 2ed77cd4dc767109d46b42ee530c00bcf1f54904 Mon Sep 17 00:00:00 2001 From: Hufe921 Date: Tue, 12 Apr 2022 22:13:22 +0800 Subject: [PATCH 1/6] feat:add checkbox particle --- src/editor/core/draw/Draw.ts | 19 ++++++++++++++--- .../core/draw/particle/CheckboxParticle.ts | 21 +++++++++++++++++++ src/editor/dataset/constant/Checkbox.ts | 7 +++++++ src/editor/dataset/enum/Element.ts | 3 ++- src/editor/index.ts | 13 ++++++++++-- src/editor/interface/Checkbox.ts | 10 +++++++++ src/editor/interface/Editor.ts | 2 ++ src/editor/interface/Element.ts | 6 ++++++ src/mock.ts | 20 ++++++++++++++++++ 9 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 src/editor/core/draw/particle/CheckboxParticle.ts create mode 100644 src/editor/dataset/constant/Checkbox.ts create mode 100644 src/editor/interface/Checkbox.ts diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts index 469a4fb..1539e99 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -37,6 +37,8 @@ import { Watermark } from './frame/Watermark' import { EditorMode } from '../../dataset/enum/Editor' import { Control } from './control/Control' import { zipElementList } from '../../utils/element' +import { CheckboxParticle } from './particle/CheckboxParticle' +import { DeepRequired } from '../../interface/Common' export class Draw { @@ -46,7 +48,7 @@ export class Draw { private ctxList: CanvasRenderingContext2D[] private pageNo: number private mode: EditorMode - private options: Required + private options: DeepRequired private position: Position private elementList: IElement[] private listener: Listener @@ -73,6 +75,7 @@ export class Draw { private pageBreakParticle: PageBreakParticle private superscriptParticle: SuperscriptParticle private subscriptParticle: SubscriptParticle + private checkboxParticle: CheckboxParticle private control: Control private rowList: IRow[] @@ -83,7 +86,7 @@ export class Draw { constructor( container: HTMLDivElement, - options: Required, + options: DeepRequired, elementList: IElement[], listener: Listener ) { @@ -120,6 +123,7 @@ export class Draw { this.pageBreakParticle = new PageBreakParticle(this) this.superscriptParticle = new SuperscriptParticle() this.subscriptParticle = new SubscriptParticle() + this.checkboxParticle = new CheckboxParticle(this) this.control = new Control(this) new ScrollObserver(this) @@ -249,7 +253,7 @@ export class Draw { return this.ctxList[this.pageNo] } - public getOptions(): Required { + public getOptions(): DeepRequired { return this.options } @@ -563,6 +567,12 @@ export class Draw { element.width = innerWidth metrics.width = innerWidth metrics.height = this.options.defaultSize + } else if (element.type === ElementType.CHECKBOX) { + const { width, height, gap } = this.options.checkbox + const elementWidth = width + gap * 2 + element.width = elementWidth + metrics.width = elementWidth + metrics.height = height } else { // 设置上下标真实字体尺寸 const size = element.size || this.options.defaultSize @@ -697,6 +707,9 @@ export class Draw { if (this.mode !== EditorMode.CLEAN) { this.pageBreakParticle.render(ctx, element, x, y) } + } else if (element.type === ElementType.CHECKBOX) { + this.textParticle.complete() + this.checkboxParticle.render(ctx, element, x, y + offsetY) } else { this.textParticle.record(ctx, element, x, y + offsetY) } diff --git a/src/editor/core/draw/particle/CheckboxParticle.ts b/src/editor/core/draw/particle/CheckboxParticle.ts new file mode 100644 index 0000000..2635d9b --- /dev/null +++ b/src/editor/core/draw/particle/CheckboxParticle.ts @@ -0,0 +1,21 @@ +import { DeepRequired } from '../../../interface/Common' +import { IEditorOption } from '../../../interface/Editor' +import { IRowElement } from '../../../interface/Row' +import { Draw } from '../Draw' + +export class CheckboxParticle { + + private options: DeepRequired + + constructor(draw: Draw) { + this.options = draw.getOptions() + } + + public render(ctx: CanvasRenderingContext2D, element: IRowElement, x: number, y: number) { + const { checkbox: { gap } } = this.options + const { metrics } = element + ctx.rect(x + gap, y - metrics.height, metrics.width - gap * 2, metrics.height) + ctx.stroke() + } + +} \ No newline at end of file diff --git a/src/editor/dataset/constant/Checkbox.ts b/src/editor/dataset/constant/Checkbox.ts new file mode 100644 index 0000000..bcb5aa6 --- /dev/null +++ b/src/editor/dataset/constant/Checkbox.ts @@ -0,0 +1,7 @@ +import { ICheckboxOption } from '../../interface/Checkbox' + +export const defaultCheckboxOption: Readonly> = { + width: 14, + height: 14, + gap: 5 +} \ No newline at end of file diff --git a/src/editor/dataset/enum/Element.ts b/src/editor/dataset/enum/Element.ts index 07c379e..e1e6c8b 100644 --- a/src/editor/dataset/enum/Element.ts +++ b/src/editor/dataset/enum/Element.ts @@ -7,5 +7,6 @@ export enum ElementType { SUBSCRIPT = 'subscript', SEPARATOR = 'separator', PAGE_BREAK = 'pageBreak', - CONTROL = 'control' + CONTROL = 'control', + CHECKBOX = 'checkbox' } \ No newline at end of file diff --git a/src/editor/index.ts b/src/editor/index.ts index b4a5f34..236c07a 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -22,6 +22,9 @@ import { defaultWatermarkOption } from './dataset/constant/Watermark' import { ControlType } from './dataset/enum/Control' import { defaultControlOption } from './dataset/constant/Control' import { IControlOption } from './interface/Control' +import { ICheckboxOption } from './interface/Checkbox' +import { defaultCheckboxOption } from './dataset/constant/Checkbox' +import { DeepRequired } from './interface/Common' export default class Editor { @@ -42,7 +45,12 @@ export default class Editor { ...defaultControlOption, ...options.control } - const editorOptions: Required = { + const checkboxOptions: Required = { + ...defaultCheckboxOption, + ...options.checkbox + } + + const editorOptions: DeepRequired = { defaultMode: EditorMode.EDIT, defaultType: 'TEXT', defaultFont: 'Yahei', @@ -76,7 +84,8 @@ export default class Editor { ...options, header: headerOptions, watermark: waterMarkOptions, - control: controlOptions + control: controlOptions, + checkbox: checkboxOptions } formatElementList(elementList, { editorOptions diff --git a/src/editor/interface/Checkbox.ts b/src/editor/interface/Checkbox.ts new file mode 100644 index 0000000..0da022b --- /dev/null +++ b/src/editor/interface/Checkbox.ts @@ -0,0 +1,10 @@ +export interface ICheckbox { + disabled?: boolean; + value: boolean | null; +} + +export interface ICheckboxOption { + width?: number; + height?: number; + gap?: number; +} \ No newline at end of file diff --git a/src/editor/interface/Editor.ts b/src/editor/interface/Editor.ts index 055914b..c8d929c 100644 --- a/src/editor/interface/Editor.ts +++ b/src/editor/interface/Editor.ts @@ -1,5 +1,6 @@ import { IElement } from '..' import { EditorMode } from '../dataset/enum/Editor' +import { ICheckboxOption } from './Checkbox' import { IControlOption } from './Control' import { IHeader } from './Header' import { IWatermark } from './Watermark' @@ -38,6 +39,7 @@ export interface IEditorOption { header?: IHeader; watermark?: IWatermark; control?: IControlOption; + checkbox?: ICheckboxOption; } export interface IEditorResult { diff --git a/src/editor/interface/Element.ts b/src/editor/interface/Element.ts index bd2d4cd..c914eb1 100644 --- a/src/editor/interface/Element.ts +++ b/src/editor/interface/Element.ts @@ -1,6 +1,7 @@ import { ControlComponent } from '../dataset/enum/Control' import { ElementType } from '../dataset/enum/Element' import { RowFlex } from '../dataset/enum/Row' +import { ICheckbox } from './Checkbox' import { IControl } from './Control' import { IColgroup } from './table/Colgroup' import { ITr } from './table/Tr' @@ -59,6 +60,10 @@ export interface IControlElement { controlComponent?: ControlComponent; } +export interface ICheckboxElement { + checkbox?: ICheckbox; +} + export type IElement = IElementBasic & IElementStyle & ITable @@ -66,6 +71,7 @@ export type IElement = IElementBasic & ISuperscriptSubscript & ISeparator & IControlElement + & ICheckboxElement export interface IElementMetrics { width: number; diff --git a/src/mock.ts b/src/mock.ts index 16627b2..8858643 100644 --- a/src/mock.ts +++ b/src/mock.ts @@ -246,6 +246,26 @@ elementList.push({ }] }) +// 模拟checkbox +elementList.push(...[{ + value: '是否同意以上内容:' +}, { + type: ElementType.CHECKBOX, + checkbox: { + value: true + }, + value: '' +}, { + value: '同意' +}, { + type: ElementType.CHECKBOX, + value: '' +}, { + value: '否定' +}, { + value: '\n' +}]) + // 模拟结尾文本 elementList.push(...[{ value: 'E', From 8c2803f5ca60c6bcf40b3207c708d73c766fff0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BA=91=E9=A3=9E?= Date: Wed, 13 Apr 2022 17:34:31 +0800 Subject: [PATCH 2/6] feat:draw checkbox select state --- .../core/draw/particle/CheckboxParticle.ts | 24 ++++++++++++++++--- src/editor/dataset/constant/Checkbox.ts | 3 ++- src/editor/interface/Checkbox.ts | 1 + 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/editor/core/draw/particle/CheckboxParticle.ts b/src/editor/core/draw/particle/CheckboxParticle.ts index 2635d9b..d6d6ba1 100644 --- a/src/editor/core/draw/particle/CheckboxParticle.ts +++ b/src/editor/core/draw/particle/CheckboxParticle.ts @@ -12,10 +12,28 @@ export class CheckboxParticle { } public render(ctx: CanvasRenderingContext2D, element: IRowElement, x: number, y: number) { - const { checkbox: { gap } } = this.options - const { metrics } = element - ctx.rect(x + gap, y - metrics.height, metrics.width - gap * 2, metrics.height) + const { checkbox: { gap, lineWidth } } = this.options + const { metrics, checkbox } = element + // left top 四舍五入避免1像素问题 + const left = Math.round(x + gap) + const top = Math.round(y - metrics.height + lineWidth) + const width = metrics.width - gap * 2 + const height = metrics.height + ctx.save() + ctx.beginPath() + ctx.translate(0.5, 0.5) + ctx.lineWidth = lineWidth + ctx.rect(left, top, width, height) ctx.stroke() + // 绘制勾选状态 + if (checkbox?.value) { + ctx.moveTo(left + 2, top + 7) + ctx.lineTo(left + 6, top + 12) + ctx.lineTo(left + 12, top + 3) + ctx.stroke() + } + ctx.closePath() + ctx.restore() } } \ No newline at end of file diff --git a/src/editor/dataset/constant/Checkbox.ts b/src/editor/dataset/constant/Checkbox.ts index bcb5aa6..52e223b 100644 --- a/src/editor/dataset/constant/Checkbox.ts +++ b/src/editor/dataset/constant/Checkbox.ts @@ -3,5 +3,6 @@ import { ICheckboxOption } from '../../interface/Checkbox' export const defaultCheckboxOption: Readonly> = { width: 14, height: 14, - gap: 5 + gap: 5, + lineWidth: 1 } \ No newline at end of file diff --git a/src/editor/interface/Checkbox.ts b/src/editor/interface/Checkbox.ts index 0da022b..b07c50f 100644 --- a/src/editor/interface/Checkbox.ts +++ b/src/editor/interface/Checkbox.ts @@ -7,4 +7,5 @@ export interface ICheckboxOption { width?: number; height?: number; gap?: number; + lineWidth?: number; } \ No newline at end of file From 24f46e1b35f8a259674f8d66204bce369daac90a Mon Sep 17 00:00:00 2001 From: Hufe921 Date: Wed, 13 Apr 2022 21:43:47 +0800 Subject: [PATCH 3/6] feat:add checkbox interactive --- src/editor/core/draw/Draw.ts | 4 +-- .../core/draw/particle/CheckboxParticle.ts | 31 ++++++++++++----- src/editor/core/event/CanvasEvent.ts | 34 ++++++++++++------- src/editor/core/position/Position.ts | 7 ++++ src/editor/dataset/constant/Checkbox.ts | 4 ++- src/editor/interface/Checkbox.ts | 2 ++ src/editor/interface/Position.ts | 1 + 7 files changed, 59 insertions(+), 24 deletions(-) diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts index 1539e99..6b04f6a 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -569,10 +569,10 @@ export class Draw { metrics.height = this.options.defaultSize } else if (element.type === ElementType.CHECKBOX) { const { width, height, gap } = this.options.checkbox - const elementWidth = width + gap * 2 + const elementWidth = (width + gap * 2) * scale element.width = elementWidth metrics.width = elementWidth - metrics.height = height + metrics.height = height * scale } else { // 设置上下标真实字体尺寸 const size = element.size || this.options.defaultSize diff --git a/src/editor/core/draw/particle/CheckboxParticle.ts b/src/editor/core/draw/particle/CheckboxParticle.ts index d6d6ba1..85d1704 100644 --- a/src/editor/core/draw/particle/CheckboxParticle.ts +++ b/src/editor/core/draw/particle/CheckboxParticle.ts @@ -12,24 +12,39 @@ export class CheckboxParticle { } public render(ctx: CanvasRenderingContext2D, element: IRowElement, x: number, y: number) { - const { checkbox: { gap, lineWidth } } = this.options + const { checkbox: { gap, lineWidth, fillStyle, fontStyle }, scale } = this.options const { metrics, checkbox } = element // left top 四舍五入避免1像素问题 const left = Math.round(x + gap) const top = Math.round(y - metrics.height + lineWidth) - const width = metrics.width - gap * 2 + const width = metrics.width - gap * 2 * scale const height = metrics.height ctx.save() ctx.beginPath() ctx.translate(0.5, 0.5) - ctx.lineWidth = lineWidth - ctx.rect(left, top, width, height) - ctx.stroke() // 绘制勾选状态 if (checkbox?.value) { - ctx.moveTo(left + 2, top + 7) - ctx.lineTo(left + 6, top + 12) - ctx.lineTo(left + 12, top + 3) + // 边框 + ctx.lineWidth = lineWidth + ctx.strokeStyle = fillStyle + ctx.rect(left, top, width, height) + ctx.stroke() + // 背景色 + ctx.beginPath() + ctx.fillStyle = fillStyle + ctx.fillRect(left, top, width, height) + // 勾选对号 + ctx.beginPath() + ctx.strokeStyle = fontStyle + ctx.lineWidth = lineWidth * 2 + ctx.moveTo(left + 2 * scale, top + 7 * scale) + ctx.lineTo(left + 7 * scale, top + 11 * scale) + ctx.moveTo(left + 6.5 * scale, top + 11 * scale) + ctx.lineTo(left + 12 * scale, top + 3 * scale) + ctx.stroke() + } else { + ctx.lineWidth = lineWidth + ctx.rect(left, top, width, height) ctx.stroke() } ctx.closePath() diff --git a/src/editor/core/event/CanvasEvent.ts b/src/editor/core/event/CanvasEvent.ts index 49ec119..30c511e 100644 --- a/src/editor/core/event/CanvasEvent.ts +++ b/src/editor/core/event/CanvasEvent.ts @@ -188,6 +188,7 @@ export class CanvasEvent { const { index, isDirectHit, + isCheckbox, isControl, isImage, isTable, @@ -214,27 +215,34 @@ export class CanvasEvent { ...positionResult, index: isTable ? tdValueIndex! : index } + const elementList = this.draw.getElementList() + const positionList = this.position.getPositionList() + const curIndex = isTable ? tdValueIndex! : index + const curElement = elementList[curIndex] // 绘制 - const isDirectHitImage = isDirectHit && isImage + const isDirectHitImage = !!(isDirectHit && isImage) + const isDirectHitCheckbox = !!(isDirectHit && isCheckbox) if (~index) { - let curIndex = index - if (isTable) { - this.range.setRange(tdValueIndex!, tdValueIndex!) - curIndex = tdValueIndex! - } else { - this.range.setRange(index, index) + this.range.setRange(index, index) + // 复选框 + const isSetCheckbox = isDirectHitCheckbox && !isReadonly + if (isSetCheckbox) { + const { checkbox } = curElement + if (checkbox) { + checkbox.value = !checkbox.value + } else { + curElement.checkbox = { + value: true + } + } } this.draw.render({ curIndex, - isSubmitHistory: false, - isSetCursor: !isDirectHitImage, + isSubmitHistory: isSetCheckbox, + isSetCursor: !isDirectHitImage && !isDirectHitCheckbox, isComputeRowList: false }) } - const elementList = this.draw.getElementList() - const positionList = this.position.getPositionList() - const curIndex = isTable ? tdValueIndex! : index - const curElement = elementList[curIndex] // 图片尺寸拖拽组件 this.imageParticle.clearResizer() if (isDirectHitImage && !isReadonly) { diff --git a/src/editor/core/position/Position.ts b/src/editor/core/position/Position.ts index cccacf9..b0fb663 100644 --- a/src/editor/core/position/Position.ts +++ b/src/editor/core/position/Position.ts @@ -119,6 +119,13 @@ export class Position { isImage: true } } + if (element.type === ElementType.CHECKBOX) { + return { + index: curPositionIndex, + isDirectHit: true, + isCheckbox: true + } + } // 判断是否在文字中间前后 if (elementList[index].value !== ZERO) { const valueWidth = rightTop[0] - leftTop[0] diff --git a/src/editor/dataset/constant/Checkbox.ts b/src/editor/dataset/constant/Checkbox.ts index 52e223b..3c478bc 100644 --- a/src/editor/dataset/constant/Checkbox.ts +++ b/src/editor/dataset/constant/Checkbox.ts @@ -4,5 +4,7 @@ export const defaultCheckboxOption: Readonly> = { width: 14, height: 14, gap: 5, - lineWidth: 1 + lineWidth: 1, + fillStyle: '#5175f4', + fontStyle: '#ffffff' } \ No newline at end of file diff --git a/src/editor/interface/Checkbox.ts b/src/editor/interface/Checkbox.ts index b07c50f..055eedf 100644 --- a/src/editor/interface/Checkbox.ts +++ b/src/editor/interface/Checkbox.ts @@ -8,4 +8,6 @@ export interface ICheckboxOption { height?: number; gap?: number; lineWidth?: number; + fillStyle?: string; + fontStyle?: string; } \ No newline at end of file diff --git a/src/editor/interface/Position.ts b/src/editor/interface/Position.ts index 4e95072..1d15f1d 100644 --- a/src/editor/interface/Position.ts +++ b/src/editor/interface/Position.ts @@ -4,6 +4,7 @@ import { ITd } from './table/Td' export interface ICurrentPosition { index: number; + isCheckbox?: boolean; isControl?: boolean; isImage?: boolean; isTable?: boolean; From f9b27372e2b323d7070bcfdcc4de6efd654d05f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BA=91=E9=A3=9E?= Date: Thu, 14 Apr 2022 17:03:05 +0800 Subject: [PATCH 4/6] feat:add checkbox menu --- index.html | 3 +++ src/assets/images/checkbox.svg | 1 + src/editor/core/event/CanvasEvent.ts | 2 +- src/editor/core/position/Position.ts | 4 +++- src/editor/dataset/constant/Element.ts | 3 ++- src/main.ts | 12 +++++++++++- src/style.css | 4 ++++ 7 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 src/assets/images/checkbox.svg diff --git a/index.html b/index.html index 01d078a..68a38c4 100644 --- a/index.html +++ b/index.html @@ -169,6 +169,9 @@ +