diff --git a/src/editor/core/cursor/Cursor.ts b/src/editor/core/cursor/Cursor.ts index fa6b6e9..44fbe3d 100644 --- a/src/editor/core/cursor/Cursor.ts +++ b/src/editor/core/cursor/Cursor.ts @@ -36,7 +36,7 @@ export class Cursor { public setCursorPosition(evt: MouseEvent) { const positionIndex = this.position.getPositionByXY(evt.offsetX, evt.offsetY) if (~positionIndex) { - this.range.setRange(0, 0) + this.range.setRange(positionIndex, positionIndex) setTimeout(() => { this.draw.render({ curIndex: positionIndex, isSubmitHistory: false }) }) diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts index 44cb227..1ddb0b1 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -8,6 +8,7 @@ import { Cursor } from "../cursor/Cursor" import { CanvasEvent } from "../event/CanvasEvent" import { GlobalEvent } from "../event/GlobalEvent" import { HistoryManager } from "../history/HistoryManager" +import { Listener } from "../listener/Listener" import { Position } from "../position/Position" import { RangeManager } from "../range/RangeManager" import { Background } from "./Background" @@ -24,6 +25,7 @@ export class Draw { private options: Required private position: Position private elementList: IElement[] + private listener: Listener private cursor: Cursor private range: RangeManager @@ -39,11 +41,18 @@ export class Draw { private painterStyle: IElementStyle | null private searchMatchList: number[][] | null - constructor(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, options: Required, elementList: IElement[]) { + constructor( + canvas: HTMLCanvasElement, + ctx: CanvasRenderingContext2D, + options: Required, + elementList: IElement[], + listener: Listener + ) { this.canvas = canvas this.ctx = ctx this.options = options this.elementList = elementList + this.listener = listener this.historyManager = new HistoryManager() this.position = new Position(this) @@ -64,6 +73,8 @@ export class Draw { this.rowCount = 0 this.painterStyle = null this.searchMatchList = null + + this.setDefaultRange() } public getOptions(): Required { @@ -86,6 +97,10 @@ export class Draw { return this.elementList } + public getListener(): Listener { + return this.listener + } + public getCursor(): Cursor { return this.cursor } @@ -117,6 +132,15 @@ export class Draw { this.searchMatchList = payload } + private setDefaultRange() { + if (!this.elementList.length) return + setTimeout(() => { + const curIndex = this.elementList.length - 1 + this.range.setRange(curIndex, curIndex) + this.range.setRangeStyle() + }) + } + private getFont(el: IElement): string { const { defaultSize, defaultFont } = this.options return `${el.italic ? 'italic ' : ''}${el.bold ? 'bold ' : ''}${el.size || defaultSize}px ${el.font || defaultFont}` @@ -223,8 +247,8 @@ export class Draw { this.ctx.fillText(element.value, x, y + curRow.ascent) // 选区绘制 const { startIndex, endIndex } = this.range.getRange() - if (startIndex < index && index <= endIndex) { - this.range.drawRange(x, y, metrics.width, curRow.height) + if (startIndex !== endIndex && startIndex < index && index <= endIndex) { + this.range.render(x, y, metrics.width, curRow.height) } index++ x += metrics.width diff --git a/src/editor/core/event/CanvasEvent.ts b/src/editor/core/event/CanvasEvent.ts index c84ca10..fb26e3b 100644 --- a/src/editor/core/event/CanvasEvent.ts +++ b/src/editor/core/event/CanvasEvent.ts @@ -115,8 +115,9 @@ export class CanvasEvent { } else { elementList.splice(index, 1) } - this.range.setRange(0, 0) - this.draw.render({ curIndex: isCollspace ? index - 1 : startIndex }) + const curIndex = isCollspace ? index - 1 : startIndex + this.range.setRange(curIndex, curIndex) + this.draw.render({ curIndex }) } else if (evt.key === KeyMap.Enter) { const enterText: IElement = { value: ZERO @@ -126,17 +127,20 @@ export class CanvasEvent { } else { elementList.splice(startIndex + 1, endIndex - startIndex, enterText) } - this.range.setRange(0, 0) - this.draw.render({ curIndex: index + 1 }) + const curIndex = index + 1 + this.range.setRange(curIndex, curIndex) + this.draw.render({ curIndex }) } else if (evt.key === KeyMap.Left) { if (index > 0) { - this.range.setRange(0, 0) - this.draw.render({ curIndex: index - 1, isSubmitHistory: false }) + const curIndex = index - 1 + this.range.setRange(curIndex, curIndex) + this.draw.render({ curIndex, isSubmitHistory: false }) } } else if (evt.key === KeyMap.Right) { if (index < position.length - 1) { - this.range.setRange(0, 0) - this.draw.render({ curIndex: index + 1, isSubmitHistory: false }) + const curIndex = index + 1 + this.range.setRange(curIndex, curIndex) + this.draw.render({ curIndex, isSubmitHistory: false }) } } else if (evt.key === KeyMap.Up || evt.key === KeyMap.Down) { const { rowNo, index, coordinate: { leftTop, rightTop } } = cursorPosition @@ -171,8 +175,9 @@ export class CanvasEvent { maxIndex = position.index } } - this.range.setRange(0, 0) - this.draw.render({ curIndex: maxIndex, isSubmitHistory: false }) + const curIndex = maxIndex + this.range.setRange(curIndex, curIndex) + this.draw.render({ curIndex, isSubmitHistory: false }) } } else if (evt.ctrlKey && evt.key === KeyMap.Z) { this.historyManager.undo() @@ -188,8 +193,9 @@ export class CanvasEvent { if (!isCollspace) { writeText(position.slice(startIndex + 1, endIndex + 1).map(p => p.value).join('')) elementList.splice(startIndex + 1, endIndex - startIndex) - this.range.setRange(0, 0) - this.draw.render({ curIndex: startIndex }) + const curIndex = startIndex + this.range.setRange(curIndex, curIndex) + this.draw.render({ curIndex }) } } else if (evt.ctrlKey && evt.key === KeyMap.A) { this.range.setRange(0, position.length - 1) @@ -215,8 +221,9 @@ export class CanvasEvent { } else { elementList.splice(startIndex + 1, endIndex - startIndex, ...inputData) } - this.range.setRange(0, 0) - this.draw.render({ curIndex: (isCollspace ? index : startIndex) + inputData.length }) + const curIndex = (isCollspace ? index : startIndex) + inputData.length + this.range.setRange(curIndex, curIndex) + this.draw.render({ curIndex }) } public paste(evt: ClipboardEvent) { diff --git a/src/editor/core/event/GlobalEvent.ts b/src/editor/core/event/GlobalEvent.ts index 9a24bc3..75c2eaa 100644 --- a/src/editor/core/event/GlobalEvent.ts +++ b/src/editor/core/event/GlobalEvent.ts @@ -2,6 +2,7 @@ import { EDITOR_COMPONENT } from "../../dataset/constant/Editor" import { findParent } from "../../utils" import { Cursor } from "../cursor/Cursor" import { Draw } from "../draw/Draw" +import { RangeManager } from "../range/RangeManager" import { CanvasEvent } from "./CanvasEvent" export class GlobalEvent { @@ -10,12 +11,14 @@ export class GlobalEvent { private draw: Draw private cursor: Cursor | null private canvasEvent: CanvasEvent + private range: RangeManager constructor(canvas: HTMLCanvasElement, draw: Draw, canvasEvent: CanvasEvent) { this.canvas = canvas this.draw = draw this.canvasEvent = canvasEvent this.cursor = null + this.range = draw.getRange() } register() { @@ -23,10 +26,11 @@ export class GlobalEvent { document.addEventListener('click', (evt) => { this.recoverCursor(evt) + this.setRangeStyle() }) document.addEventListener('mouseup', () => { - this.updateDragState() + this.setDragState() }) } @@ -46,8 +50,12 @@ export class GlobalEvent { this.cursor.recoveryCursor() } - updateDragState() { + setDragState() { this.canvasEvent.setIsAllowDrag(false) } + setRangeStyle() { + this.range.setRangeStyle() + } + } \ No newline at end of file diff --git a/src/editor/core/history/HistoryManager.ts b/src/editor/core/history/HistoryManager.ts index e87bcdd..b7d661c 100644 --- a/src/editor/core/history/HistoryManager.ts +++ b/src/editor/core/history/HistoryManager.ts @@ -4,7 +4,7 @@ export class HistoryManager { private undoStack: Array = [] private redoStack: Array = [] - undo() { + public undo() { if (this.undoStack.length > 1) { const pop = this.undoStack.pop()! this.redoStack.push(pop) @@ -14,7 +14,7 @@ export class HistoryManager { } } - redo() { + public redo() { if (this.redoStack.length) { const pop = this.redoStack.pop()! this.undoStack.push(pop) @@ -22,7 +22,7 @@ export class HistoryManager { } } - execute(fn: Function) { + public execute(fn: Function) { this.undoStack.push(fn) if (this.redoStack.length) { this.redoStack = [] @@ -32,4 +32,12 @@ export class HistoryManager { } } + public isCanUndo(): boolean { + return this.undoStack.length > 1 + } + + public isCanRedo(): boolean { + return !!this.redoStack.length + } + } \ No newline at end of file diff --git a/src/editor/core/listener/Listener.ts b/src/editor/core/listener/Listener.ts new file mode 100644 index 0000000..f750060 --- /dev/null +++ b/src/editor/core/listener/Listener.ts @@ -0,0 +1,11 @@ +import { IRangeStyleChange } from "../../interface/Listener" + +export class Listener { + + public rangeStyleChange: IRangeStyleChange | null + + constructor() { + this.rangeStyleChange = null + } + +} \ No newline at end of file diff --git a/src/editor/core/range/RangeManager.ts b/src/editor/core/range/RangeManager.ts index 28d4c2d..a9eb3da 100644 --- a/src/editor/core/range/RangeManager.ts +++ b/src/editor/core/range/RangeManager.ts @@ -2,6 +2,8 @@ import { IEditorOption } from "../../interface/Editor" import { IElement } from "../../interface/Element" import { IRange } from "../../interface/Range" import { Draw } from "../draw/Draw" +import { HistoryManager } from "../history/HistoryManager" +import { Listener } from "../listener/Listener" export class RangeManager { @@ -9,11 +11,15 @@ export class RangeManager { private options: Required private range: IRange private draw: Draw + private listener: Listener + private historyManager: HistoryManager constructor(ctx: CanvasRenderingContext2D, options: Required, draw: Draw) { this.ctx = ctx this.options = options this.draw = draw + this.listener = draw.getListener() + this.historyManager = draw.getHistoryManager() this.range = { startIndex: 0, endIndex: 0 @@ -36,15 +42,45 @@ export class RangeManager { this.range.endIndex = endIndex } - public drawRange(x: number, y: number, width: number, height: number) { - const { startIndex, endIndex } = this.range - if (startIndex !== endIndex) { - this.ctx.save() - this.ctx.globalAlpha = this.options.rangeAlpha - this.ctx.fillStyle = this.options.rangeColor - this.ctx.fillRect(x, y, width, height) - this.ctx.restore() + public setRangeStyle() { + if (!this.listener.rangeStyleChange) return + let curElementList = this.getSelection() + if (!curElementList) { + const elementList = this.draw.getElementList() + const { endIndex } = this.range + curElementList = [elementList[endIndex]] } + const curElement = curElementList[curElementList.length - 1] + // 富文本 + let bold = !~curElementList.findIndex(el => !el.bold) + let italic = !~curElementList.findIndex(el => !el.italic) + let underline = !~curElementList.findIndex(el => !el.underline) + let strikeout = !~curElementList.findIndex(el => !el.strikeout) + const color = curElement.color || null + const highlight = curElement.highlight || null + // 菜单 + const format = !!this.draw.getPainterStyle() + const undo = this.historyManager.isCanUndo() + const redo = this.historyManager.isCanRedo() + this.listener.rangeStyleChange({ + undo, + redo, + format, + bold, + italic, + underline, + strikeout, + color, + highlight + }) + } + + public render(x: number, y: number, width: number, height: number) { + this.ctx.save() + this.ctx.globalAlpha = this.options.rangeAlpha + this.ctx.fillStyle = this.options.rangeColor + this.ctx.fillRect(x, y, width, height) + this.ctx.restore() } } \ No newline at end of file diff --git a/src/editor/index.ts b/src/editor/index.ts index 830e35a..dd50901 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -5,10 +5,12 @@ import { IElement } from './interface/Element' import { Draw } from './core/draw/Draw' import { Command } from './core/command/Command' import { CommandAdapt } from './core/command/CommandAdapt' +import { Listener } from './core/listener/Listener' export default class Editor { public command: Command + public listener: Listener constructor(canvas: HTMLCanvasElement, elementList: IElement[], options: IEditorOption = {}) { const editorOptions: Required = { @@ -43,10 +45,12 @@ export default class Editor { text.value = ZERO } }) + // 监听 + this.listener = new Listener() // 启动 - const draw = new Draw(canvas, ctx, editorOptions, elementList) + const draw = new Draw(canvas, ctx, editorOptions, elementList, this.listener) draw.render() - // 对外命令 + // 命令 this.command = new Command(new CommandAdapt(draw)) } diff --git a/src/editor/interface/Listener.ts b/src/editor/interface/Listener.ts new file mode 100644 index 0000000..3f3e4cc --- /dev/null +++ b/src/editor/interface/Listener.ts @@ -0,0 +1,13 @@ +export interface IRangeStype { + undo: boolean; + redo: boolean; + format: boolean; + bold: boolean; + italic: boolean; + underline: boolean; + strikeout: boolean; + color: string | null; + highlight: string | null; +} + +export type IRangeStyleChange = (payload: IRangeStype) => void; diff --git a/src/main.ts b/src/main.ts index da53f38..d30e95e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -130,4 +130,9 @@ window.onload = function () { instance.command.executePrint() } + // 内部事件监听 + instance.listener.rangeStyleChange = function (payload) { + console.log('payload: ', payload); + } + } \ No newline at end of file