feat:选区样式变化监听
This commit is contained in:
@@ -36,7 +36,7 @@ export class Cursor {
|
|||||||
public setCursorPosition(evt: MouseEvent) {
|
public setCursorPosition(evt: MouseEvent) {
|
||||||
const positionIndex = this.position.getPositionByXY(evt.offsetX, evt.offsetY)
|
const positionIndex = this.position.getPositionByXY(evt.offsetX, evt.offsetY)
|
||||||
if (~positionIndex) {
|
if (~positionIndex) {
|
||||||
this.range.setRange(0, 0)
|
this.range.setRange(positionIndex, positionIndex)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.draw.render({ curIndex: positionIndex, isSubmitHistory: false })
|
this.draw.render({ curIndex: positionIndex, isSubmitHistory: false })
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Cursor } from "../cursor/Cursor"
|
|||||||
import { CanvasEvent } from "../event/CanvasEvent"
|
import { CanvasEvent } from "../event/CanvasEvent"
|
||||||
import { GlobalEvent } from "../event/GlobalEvent"
|
import { GlobalEvent } from "../event/GlobalEvent"
|
||||||
import { HistoryManager } from "../history/HistoryManager"
|
import { HistoryManager } from "../history/HistoryManager"
|
||||||
|
import { Listener } from "../listener/Listener"
|
||||||
import { Position } from "../position/Position"
|
import { Position } from "../position/Position"
|
||||||
import { RangeManager } from "../range/RangeManager"
|
import { RangeManager } from "../range/RangeManager"
|
||||||
import { Background } from "./Background"
|
import { Background } from "./Background"
|
||||||
@@ -24,6 +25,7 @@ export class Draw {
|
|||||||
private options: Required<IEditorOption>
|
private options: Required<IEditorOption>
|
||||||
private position: Position
|
private position: Position
|
||||||
private elementList: IElement[]
|
private elementList: IElement[]
|
||||||
|
private listener: Listener
|
||||||
|
|
||||||
private cursor: Cursor
|
private cursor: Cursor
|
||||||
private range: RangeManager
|
private range: RangeManager
|
||||||
@@ -39,11 +41,18 @@ export class Draw {
|
|||||||
private painterStyle: IElementStyle | null
|
private painterStyle: IElementStyle | null
|
||||||
private searchMatchList: number[][] | null
|
private searchMatchList: number[][] | null
|
||||||
|
|
||||||
constructor(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, options: Required<IEditorOption>, elementList: IElement[]) {
|
constructor(
|
||||||
|
canvas: HTMLCanvasElement,
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
options: Required<IEditorOption>,
|
||||||
|
elementList: IElement[],
|
||||||
|
listener: Listener
|
||||||
|
) {
|
||||||
this.canvas = canvas
|
this.canvas = canvas
|
||||||
this.ctx = ctx
|
this.ctx = ctx
|
||||||
this.options = options
|
this.options = options
|
||||||
this.elementList = elementList
|
this.elementList = elementList
|
||||||
|
this.listener = listener
|
||||||
|
|
||||||
this.historyManager = new HistoryManager()
|
this.historyManager = new HistoryManager()
|
||||||
this.position = new Position(this)
|
this.position = new Position(this)
|
||||||
@@ -64,6 +73,8 @@ export class Draw {
|
|||||||
this.rowCount = 0
|
this.rowCount = 0
|
||||||
this.painterStyle = null
|
this.painterStyle = null
|
||||||
this.searchMatchList = null
|
this.searchMatchList = null
|
||||||
|
|
||||||
|
this.setDefaultRange()
|
||||||
}
|
}
|
||||||
|
|
||||||
public getOptions(): Required<IEditorOption> {
|
public getOptions(): Required<IEditorOption> {
|
||||||
@@ -86,6 +97,10 @@ export class Draw {
|
|||||||
return this.elementList
|
return this.elementList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getListener(): Listener {
|
||||||
|
return this.listener
|
||||||
|
}
|
||||||
|
|
||||||
public getCursor(): Cursor {
|
public getCursor(): Cursor {
|
||||||
return this.cursor
|
return this.cursor
|
||||||
}
|
}
|
||||||
@@ -117,6 +132,15 @@ export class Draw {
|
|||||||
this.searchMatchList = payload
|
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 {
|
private getFont(el: IElement): string {
|
||||||
const { defaultSize, defaultFont } = this.options
|
const { defaultSize, defaultFont } = this.options
|
||||||
return `${el.italic ? 'italic ' : ''}${el.bold ? 'bold ' : ''}${el.size || defaultSize}px ${el.font || defaultFont}`
|
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)
|
this.ctx.fillText(element.value, x, y + curRow.ascent)
|
||||||
// 选区绘制
|
// 选区绘制
|
||||||
const { startIndex, endIndex } = this.range.getRange()
|
const { startIndex, endIndex } = this.range.getRange()
|
||||||
if (startIndex < index && index <= endIndex) {
|
if (startIndex !== endIndex && startIndex < index && index <= endIndex) {
|
||||||
this.range.drawRange(x, y, metrics.width, curRow.height)
|
this.range.render(x, y, metrics.width, curRow.height)
|
||||||
}
|
}
|
||||||
index++
|
index++
|
||||||
x += metrics.width
|
x += metrics.width
|
||||||
|
|||||||
@@ -115,8 +115,9 @@ export class CanvasEvent {
|
|||||||
} else {
|
} else {
|
||||||
elementList.splice(index, 1)
|
elementList.splice(index, 1)
|
||||||
}
|
}
|
||||||
this.range.setRange(0, 0)
|
const curIndex = isCollspace ? index - 1 : startIndex
|
||||||
this.draw.render({ curIndex: isCollspace ? index - 1 : startIndex })
|
this.range.setRange(curIndex, curIndex)
|
||||||
|
this.draw.render({ curIndex })
|
||||||
} else if (evt.key === KeyMap.Enter) {
|
} else if (evt.key === KeyMap.Enter) {
|
||||||
const enterText: IElement = {
|
const enterText: IElement = {
|
||||||
value: ZERO
|
value: ZERO
|
||||||
@@ -126,17 +127,20 @@ export class CanvasEvent {
|
|||||||
} else {
|
} else {
|
||||||
elementList.splice(startIndex + 1, endIndex - startIndex, enterText)
|
elementList.splice(startIndex + 1, endIndex - startIndex, enterText)
|
||||||
}
|
}
|
||||||
this.range.setRange(0, 0)
|
const curIndex = index + 1
|
||||||
this.draw.render({ curIndex: index + 1 })
|
this.range.setRange(curIndex, curIndex)
|
||||||
|
this.draw.render({ curIndex })
|
||||||
} else if (evt.key === KeyMap.Left) {
|
} else if (evt.key === KeyMap.Left) {
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
this.range.setRange(0, 0)
|
const curIndex = index - 1
|
||||||
this.draw.render({ curIndex: index - 1, isSubmitHistory: false })
|
this.range.setRange(curIndex, curIndex)
|
||||||
|
this.draw.render({ curIndex, isSubmitHistory: false })
|
||||||
}
|
}
|
||||||
} else if (evt.key === KeyMap.Right) {
|
} else if (evt.key === KeyMap.Right) {
|
||||||
if (index < position.length - 1) {
|
if (index < position.length - 1) {
|
||||||
this.range.setRange(0, 0)
|
const curIndex = index + 1
|
||||||
this.draw.render({ curIndex: index + 1, isSubmitHistory: false })
|
this.range.setRange(curIndex, curIndex)
|
||||||
|
this.draw.render({ curIndex, isSubmitHistory: false })
|
||||||
}
|
}
|
||||||
} else if (evt.key === KeyMap.Up || evt.key === KeyMap.Down) {
|
} else if (evt.key === KeyMap.Up || evt.key === KeyMap.Down) {
|
||||||
const { rowNo, index, coordinate: { leftTop, rightTop } } = cursorPosition
|
const { rowNo, index, coordinate: { leftTop, rightTop } } = cursorPosition
|
||||||
@@ -171,8 +175,9 @@ export class CanvasEvent {
|
|||||||
maxIndex = position.index
|
maxIndex = position.index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.range.setRange(0, 0)
|
const curIndex = maxIndex
|
||||||
this.draw.render({ curIndex: maxIndex, isSubmitHistory: false })
|
this.range.setRange(curIndex, curIndex)
|
||||||
|
this.draw.render({ curIndex, isSubmitHistory: false })
|
||||||
}
|
}
|
||||||
} else if (evt.ctrlKey && evt.key === KeyMap.Z) {
|
} else if (evt.ctrlKey && evt.key === KeyMap.Z) {
|
||||||
this.historyManager.undo()
|
this.historyManager.undo()
|
||||||
@@ -188,8 +193,9 @@ export class CanvasEvent {
|
|||||||
if (!isCollspace) {
|
if (!isCollspace) {
|
||||||
writeText(position.slice(startIndex + 1, endIndex + 1).map(p => p.value).join(''))
|
writeText(position.slice(startIndex + 1, endIndex + 1).map(p => p.value).join(''))
|
||||||
elementList.splice(startIndex + 1, endIndex - startIndex)
|
elementList.splice(startIndex + 1, endIndex - startIndex)
|
||||||
this.range.setRange(0, 0)
|
const curIndex = startIndex
|
||||||
this.draw.render({ curIndex: startIndex })
|
this.range.setRange(curIndex, curIndex)
|
||||||
|
this.draw.render({ curIndex })
|
||||||
}
|
}
|
||||||
} else if (evt.ctrlKey && evt.key === KeyMap.A) {
|
} else if (evt.ctrlKey && evt.key === KeyMap.A) {
|
||||||
this.range.setRange(0, position.length - 1)
|
this.range.setRange(0, position.length - 1)
|
||||||
@@ -215,8 +221,9 @@ export class CanvasEvent {
|
|||||||
} else {
|
} else {
|
||||||
elementList.splice(startIndex + 1, endIndex - startIndex, ...inputData)
|
elementList.splice(startIndex + 1, endIndex - startIndex, ...inputData)
|
||||||
}
|
}
|
||||||
this.range.setRange(0, 0)
|
const curIndex = (isCollspace ? index : startIndex) + inputData.length
|
||||||
this.draw.render({ curIndex: (isCollspace ? index : startIndex) + inputData.length })
|
this.range.setRange(curIndex, curIndex)
|
||||||
|
this.draw.render({ curIndex })
|
||||||
}
|
}
|
||||||
|
|
||||||
public paste(evt: ClipboardEvent) {
|
public paste(evt: ClipboardEvent) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { EDITOR_COMPONENT } from "../../dataset/constant/Editor"
|
|||||||
import { findParent } from "../../utils"
|
import { findParent } from "../../utils"
|
||||||
import { Cursor } from "../cursor/Cursor"
|
import { Cursor } from "../cursor/Cursor"
|
||||||
import { Draw } from "../draw/Draw"
|
import { Draw } from "../draw/Draw"
|
||||||
|
import { RangeManager } from "../range/RangeManager"
|
||||||
import { CanvasEvent } from "./CanvasEvent"
|
import { CanvasEvent } from "./CanvasEvent"
|
||||||
|
|
||||||
export class GlobalEvent {
|
export class GlobalEvent {
|
||||||
@@ -10,12 +11,14 @@ export class GlobalEvent {
|
|||||||
private draw: Draw
|
private draw: Draw
|
||||||
private cursor: Cursor | null
|
private cursor: Cursor | null
|
||||||
private canvasEvent: CanvasEvent
|
private canvasEvent: CanvasEvent
|
||||||
|
private range: RangeManager
|
||||||
|
|
||||||
constructor(canvas: HTMLCanvasElement, draw: Draw, canvasEvent: CanvasEvent) {
|
constructor(canvas: HTMLCanvasElement, draw: Draw, canvasEvent: CanvasEvent) {
|
||||||
this.canvas = canvas
|
this.canvas = canvas
|
||||||
this.draw = draw
|
this.draw = draw
|
||||||
this.canvasEvent = canvasEvent
|
this.canvasEvent = canvasEvent
|
||||||
this.cursor = null
|
this.cursor = null
|
||||||
|
this.range = draw.getRange()
|
||||||
}
|
}
|
||||||
|
|
||||||
register() {
|
register() {
|
||||||
@@ -23,10 +26,11 @@ export class GlobalEvent {
|
|||||||
|
|
||||||
document.addEventListener('click', (evt) => {
|
document.addEventListener('click', (evt) => {
|
||||||
this.recoverCursor(evt)
|
this.recoverCursor(evt)
|
||||||
|
this.setRangeStyle()
|
||||||
})
|
})
|
||||||
|
|
||||||
document.addEventListener('mouseup', () => {
|
document.addEventListener('mouseup', () => {
|
||||||
this.updateDragState()
|
this.setDragState()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,8 +50,12 @@ export class GlobalEvent {
|
|||||||
this.cursor.recoveryCursor()
|
this.cursor.recoveryCursor()
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDragState() {
|
setDragState() {
|
||||||
this.canvasEvent.setIsAllowDrag(false)
|
this.canvasEvent.setIsAllowDrag(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setRangeStyle() {
|
||||||
|
this.range.setRangeStyle()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@ export class HistoryManager {
|
|||||||
private undoStack: Array<Function> = []
|
private undoStack: Array<Function> = []
|
||||||
private redoStack: Array<Function> = []
|
private redoStack: Array<Function> = []
|
||||||
|
|
||||||
undo() {
|
public undo() {
|
||||||
if (this.undoStack.length > 1) {
|
if (this.undoStack.length > 1) {
|
||||||
const pop = this.undoStack.pop()!
|
const pop = this.undoStack.pop()!
|
||||||
this.redoStack.push(pop)
|
this.redoStack.push(pop)
|
||||||
@@ -14,7 +14,7 @@ export class HistoryManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
redo() {
|
public redo() {
|
||||||
if (this.redoStack.length) {
|
if (this.redoStack.length) {
|
||||||
const pop = this.redoStack.pop()!
|
const pop = this.redoStack.pop()!
|
||||||
this.undoStack.push(pop)
|
this.undoStack.push(pop)
|
||||||
@@ -22,7 +22,7 @@ export class HistoryManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(fn: Function) {
|
public execute(fn: Function) {
|
||||||
this.undoStack.push(fn)
|
this.undoStack.push(fn)
|
||||||
if (this.redoStack.length) {
|
if (this.redoStack.length) {
|
||||||
this.redoStack = []
|
this.redoStack = []
|
||||||
@@ -32,4 +32,12 @@ export class HistoryManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isCanUndo(): boolean {
|
||||||
|
return this.undoStack.length > 1
|
||||||
|
}
|
||||||
|
|
||||||
|
public isCanRedo(): boolean {
|
||||||
|
return !!this.redoStack.length
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { IRangeStyleChange } from "../../interface/Listener"
|
||||||
|
|
||||||
|
export class Listener {
|
||||||
|
|
||||||
|
public rangeStyleChange: IRangeStyleChange | null
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.rangeStyleChange = null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ import { IEditorOption } from "../../interface/Editor"
|
|||||||
import { IElement } from "../../interface/Element"
|
import { IElement } from "../../interface/Element"
|
||||||
import { IRange } from "../../interface/Range"
|
import { IRange } from "../../interface/Range"
|
||||||
import { Draw } from "../draw/Draw"
|
import { Draw } from "../draw/Draw"
|
||||||
|
import { HistoryManager } from "../history/HistoryManager"
|
||||||
|
import { Listener } from "../listener/Listener"
|
||||||
|
|
||||||
export class RangeManager {
|
export class RangeManager {
|
||||||
|
|
||||||
@@ -9,11 +11,15 @@ export class RangeManager {
|
|||||||
private options: Required<IEditorOption>
|
private options: Required<IEditorOption>
|
||||||
private range: IRange
|
private range: IRange
|
||||||
private draw: Draw
|
private draw: Draw
|
||||||
|
private listener: Listener
|
||||||
|
private historyManager: HistoryManager
|
||||||
|
|
||||||
constructor(ctx: CanvasRenderingContext2D, options: Required<IEditorOption>, draw: Draw) {
|
constructor(ctx: CanvasRenderingContext2D, options: Required<IEditorOption>, draw: Draw) {
|
||||||
this.ctx = ctx
|
this.ctx = ctx
|
||||||
this.options = options
|
this.options = options
|
||||||
this.draw = draw
|
this.draw = draw
|
||||||
|
this.listener = draw.getListener()
|
||||||
|
this.historyManager = draw.getHistoryManager()
|
||||||
this.range = {
|
this.range = {
|
||||||
startIndex: 0,
|
startIndex: 0,
|
||||||
endIndex: 0
|
endIndex: 0
|
||||||
@@ -36,15 +42,45 @@ export class RangeManager {
|
|||||||
this.range.endIndex = endIndex
|
this.range.endIndex = endIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
public drawRange(x: number, y: number, width: number, height: number) {
|
public setRangeStyle() {
|
||||||
const { startIndex, endIndex } = this.range
|
if (!this.listener.rangeStyleChange) return
|
||||||
if (startIndex !== endIndex) {
|
let curElementList = this.getSelection()
|
||||||
this.ctx.save()
|
if (!curElementList) {
|
||||||
this.ctx.globalAlpha = this.options.rangeAlpha
|
const elementList = this.draw.getElementList()
|
||||||
this.ctx.fillStyle = this.options.rangeColor
|
const { endIndex } = this.range
|
||||||
this.ctx.fillRect(x, y, width, height)
|
curElementList = [elementList[endIndex]]
|
||||||
this.ctx.restore()
|
|
||||||
}
|
}
|
||||||
|
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+6
-2
@@ -5,10 +5,12 @@ import { IElement } from './interface/Element'
|
|||||||
import { Draw } from './core/draw/Draw'
|
import { Draw } from './core/draw/Draw'
|
||||||
import { Command } from './core/command/Command'
|
import { Command } from './core/command/Command'
|
||||||
import { CommandAdapt } from './core/command/CommandAdapt'
|
import { CommandAdapt } from './core/command/CommandAdapt'
|
||||||
|
import { Listener } from './core/listener/Listener'
|
||||||
|
|
||||||
export default class Editor {
|
export default class Editor {
|
||||||
|
|
||||||
public command: Command
|
public command: Command
|
||||||
|
public listener: Listener
|
||||||
|
|
||||||
constructor(canvas: HTMLCanvasElement, elementList: IElement[], options: IEditorOption = {}) {
|
constructor(canvas: HTMLCanvasElement, elementList: IElement[], options: IEditorOption = {}) {
|
||||||
const editorOptions: Required<IEditorOption> = {
|
const editorOptions: Required<IEditorOption> = {
|
||||||
@@ -43,10 +45,12 @@ export default class Editor {
|
|||||||
text.value = ZERO
|
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()
|
draw.render()
|
||||||
// 对外命令
|
// 命令
|
||||||
this.command = new Command(new CommandAdapt(draw))
|
this.command = new Command(new CommandAdapt(draw))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -130,4 +130,9 @@ window.onload = function () {
|
|||||||
instance.command.executePrint()
|
instance.command.executePrint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 内部事件监听
|
||||||
|
instance.listener.rangeStyleChange = function (payload) {
|
||||||
|
console.log('payload: ', payload);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user