feat:选区样式变化监听
This commit is contained in:
@@ -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 })
|
||||
})
|
||||
|
||||
@@ -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<IEditorOption>
|
||||
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<IEditorOption>, elementList: IElement[]) {
|
||||
constructor(
|
||||
canvas: HTMLCanvasElement,
|
||||
ctx: CanvasRenderingContext2D,
|
||||
options: Required<IEditorOption>,
|
||||
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<IEditorOption> {
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@ export class HistoryManager {
|
||||
private undoStack: Array<Function> = []
|
||||
private redoStack: Array<Function> = []
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 { 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<IEditorOption>
|
||||
private range: IRange
|
||||
private draw: Draw
|
||||
private listener: Listener
|
||||
private historyManager: HistoryManager
|
||||
|
||||
constructor(ctx: CanvasRenderingContext2D, options: Required<IEditorOption>, 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()
|
||||
}
|
||||
|
||||
}
|
||||
+6
-2
@@ -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<IEditorOption> = {
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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.listener.rangeStyleChange = function (payload) {
|
||||
console.log('payload: ', payload);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user