diff --git a/index.html b/index.html
index dd4fde4..166f7a8 100644
--- a/index.html
+++ b/index.html
@@ -59,6 +59,12 @@
+
diff --git a/src/editor/core/command/Command.ts b/src/editor/core/command/Command.ts
index 2892726..0db473f 100644
--- a/src/editor/core/command/Command.ts
+++ b/src/editor/core/command/Command.ts
@@ -7,15 +7,17 @@ export class Command {
private static painter: Function
private static format: Function
private static bold: Function
+ private static search: Function
private static print: Function
constructor(adapt: CommandAdapt) {
Command.undo = adapt.undo.bind(adapt)
Command.redo = adapt.redo.bind(adapt)
Command.painter = adapt.painter.bind(adapt)
- Command.print = adapt.print.bind(adapt)
Command.format = adapt.format.bind(adapt)
Command.bold = adapt.bold.bind(adapt)
+ Command.search = adapt.search.bind(adapt)
+ Command.print = adapt.print.bind(adapt)
}
// 撤销、重做、格式刷、清除格式
@@ -41,6 +43,10 @@ export class Command {
}
// 搜索、打印
+ public executeSearch(payload: string | null) {
+ return Command.search(payload)
+ }
+
public executePrint() {
return Command.print()
}
diff --git a/src/editor/core/command/CommandAdapt.ts b/src/editor/core/command/CommandAdapt.ts
index e58d641..8bf0275 100644
--- a/src/editor/core/command/CommandAdapt.ts
+++ b/src/editor/core/command/CommandAdapt.ts
@@ -64,6 +64,27 @@ export class CommandAdapt {
this.draw.render({ isSetCursor: false })
}
+ public search(payload: string | null) {
+ if (payload) {
+ const elementList = this.draw.getElementList()
+ const text = elementList.map(e => !e.type || e.type === 'TEXT' ? e.value : null)
+ .filter(Boolean)
+ .join('')
+ const matchStartIndexList = []
+ let index = text.indexOf(payload)
+ while (index !== -1) {
+ matchStartIndexList.push(index)
+ index = text.indexOf(payload, index + 1)
+ }
+ const searchMatch: number[][] = matchStartIndexList
+ .map(i => Array(payload.length).fill(i).map((_, j) => i + j))
+ this.draw.setSearchMatch(searchMatch.length ? searchMatch : null)
+ } else {
+ this.draw.setSearchMatch(null)
+ }
+ this.draw.render({ isSetCursor: false, isSubmitHistory: false })
+ }
+
public print() {
return printImageBase64(this.draw.getDataURL())
}
diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts
index 4661ff0..45cf0de 100644
--- a/src/editor/core/draw/Draw.ts
+++ b/src/editor/core/draw/Draw.ts
@@ -12,6 +12,7 @@ import { Position } from "../position/Position"
import { RangeManager } from "../range/RangeManager"
import { Background } from "./Background"
import { Margin } from "./Margin"
+import { Search } from "./Search"
export class Draw {
@@ -25,10 +26,12 @@ export class Draw {
private range: RangeManager
private margin: Margin
private background: Background
+ private search: Search
private historyManager: HistoryManager
private rowCount: number
private painterStyle: IElementStyle | null
+ private searchMatchList: number[][] | null
constructor(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, options: Required, elementList: IElement[]) {
this.canvas = canvas
@@ -41,6 +44,7 @@ export class Draw {
this.range = new RangeManager(ctx, elementList, options)
this.margin = new Margin(ctx, options)
this.background = new Background(ctx)
+ this.search = new Search(ctx, options, this)
const canvasEvent = new CanvasEvent(canvas, this)
this.cursor = new Cursor(canvas, this, canvasEvent)
@@ -50,6 +54,7 @@ export class Draw {
this.rowCount = 0
this.painterStyle = null
+ this.searchMatchList = null
}
public getHistoryManager(): HistoryManager {
@@ -91,6 +96,14 @@ export class Draw {
}
}
+ public getSearchMathch(): number[][] | null {
+ return this.searchMatchList
+ }
+
+ public setSearchMatch(payload: number[][] | null) {
+ this.searchMatchList = payload
+ }
+
public render(payload?: IDrawOption) {
let { curIndex, isSubmitHistory = true, isSetCursor = true } = payload || {}
// 清除光标
@@ -189,6 +202,10 @@ export class Draw {
x = leftTopPoint[0]
y += curRow.height
}
+ // 搜索匹配绘制
+ if (this.searchMatchList) {
+ this.search.render()
+ }
// 光标重绘
if (curIndex === undefined) {
curIndex = positionList.length - 1
diff --git a/src/editor/core/draw/Search.ts b/src/editor/core/draw/Search.ts
new file mode 100644
index 0000000..33af0f7
--- /dev/null
+++ b/src/editor/core/draw/Search.ts
@@ -0,0 +1,39 @@
+import { IEditorOption } from "../../interface/Editor"
+import { Position } from "../position/Position"
+import { Draw } from "./Draw"
+
+export class Search {
+
+ private ctx: CanvasRenderingContext2D
+ private options: Required
+ private draw: Draw
+ private position: Position
+
+ constructor(ctx: CanvasRenderingContext2D, options: Required, draw: Draw) {
+ this.ctx = ctx
+ this.options = options
+ this.draw = draw
+ this.position = draw.getPosition()
+ }
+
+ render() {
+ const searchMatch = this.draw.getSearchMathch()
+ if (!searchMatch || !searchMatch.length) return
+ const searchMatchList = searchMatch.flat()
+ const positionList = this.position.getPositionList()
+ this.ctx.save()
+ this.ctx.globalAlpha = this.options.searchMatchAlpha
+ this.ctx.fillStyle = this.options.searchMatchColor
+ searchMatchList.forEach(s => {
+ const position = positionList[s]
+ const { leftTop, leftBottom, rightTop } = position.coordinate
+ const x = leftTop[0]
+ const y = leftTop[1]
+ const width = rightTop[0] - leftTop[0]
+ const height = leftBottom[1] - leftTop[1]
+ this.ctx.fillRect(x, y, width, height)
+ })
+ this.ctx.restore()
+ }
+
+}
\ No newline at end of file
diff --git a/src/editor/core/range/RangeManager.ts b/src/editor/core/range/RangeManager.ts
index faac0e5..8c76dee 100644
--- a/src/editor/core/range/RangeManager.ts
+++ b/src/editor/core/range/RangeManager.ts
@@ -9,7 +9,7 @@ export class RangeManager {
private options: Required
private range: IRange
- constructor(ctx: CanvasRenderingContext2D, elementList: IElement[], options: Required,) {
+ constructor(ctx: CanvasRenderingContext2D, elementList: IElement[], options: Required) {
this.ctx = ctx
this.elementList = elementList
this.options = options
diff --git a/src/editor/index.ts b/src/editor/index.ts
index 73e8a2e..5bd51d6 100644
--- a/src/editor/index.ts
+++ b/src/editor/index.ts
@@ -17,6 +17,8 @@ export default class Editor {
defaultSize: 16,
rangeAlpha: 0.6,
rangeColor: '#AECBFA',
+ searchMatchAlpha: 0.6,
+ searchMatchColor: '#FFFF00',
marginIndicatorSize: 35,
marginIndicatorColor: '#BABABA',
margins: [100, 120, 100, 120],
diff --git a/src/editor/interface/Editor.ts b/src/editor/interface/Editor.ts
index aac9221..d591681 100644
--- a/src/editor/interface/Editor.ts
+++ b/src/editor/interface/Editor.ts
@@ -4,6 +4,8 @@ export interface IEditorOption {
defaultSize?: number;
rangeColor?: string;
rangeAlpha?: number;
+ searchMatchColor?: string;
+ searchMatchAlpha?: number;
marginIndicatorSize?: number;
marginIndicatorColor?: string,
margins?: [top: number, right: number, bootom: number, left: number]
diff --git a/src/main.ts b/src/main.ts
index 8c4a5bf..024d741 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -70,6 +70,25 @@ window.onload = function () {
}
// 搜索、打印
+ const collspanDom = document.querySelector('.menu-item__search__collapse')
+ const searchInputDom = document.querySelector('.menu-item__search__collapse__search input')
+ document.querySelector('.menu-item__search')!.onclick = function () {
+ console.log('search')
+ collspanDom!.style.display = 'block'
+ }
+ document.querySelector('.menu-item__search__collapse span')!.onclick = function () {
+ collspanDom!.style.display = 'none'
+ searchInputDom!.value = ''
+ instance.command.executeSearch(null)
+ }
+ searchInputDom!.oninput = function () {
+ instance.command.executeSearch(searchInputDom?.value || null)
+ }
+ searchInputDom!.onkeydown = function (evt) {
+ if (evt.key === 'Enter') {
+ instance.command.executeSearch(searchInputDom?.value || null)
+ }
+ }
document.querySelector('.menu-item__print')!.onclick = function () {
console.log('print')
instance.command.executePrint()
diff --git a/src/style.css b/src/style.css
index b36f06a..2ede447 100644
--- a/src/style.css
+++ b/src/style.css
@@ -32,6 +32,7 @@ body {
height: 24px;
display: flex;
align-items: center;
+ position: relative;
}
.menu-item>div {
@@ -125,10 +126,59 @@ body {
background-color: #ffff00;
}
+.menu-item__search {
+ position: relative;
+}
+
.menu-item__search i {
background-image: url('./assets/images/search.svg');
}
+.menu-item .menu-item__search__collapse {
+ box-sizing: border-box;
+ position: absolute;
+ display: none;
+ z-index: 99;
+ top: 25px;
+ left: 0;
+ box-shadow: 0px 5px 5px #e3dfdf;
+}
+
+.menu-item .menu-item__search__collapse__search {
+ width: 205px;
+ height: 36px;
+ padding: 0 5px;
+ line-height: 36px;
+ background: #ffffff;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ border-radius: 4px;
+}
+
+.menu-item .menu-item__search__collapse__search input {
+ height: 27px;
+ appearance: none;
+ background-color: #fff;
+ background-image: none;
+ border-radius: 4px;
+ border: 1px solid #ebebeb;
+ box-sizing: border-box;
+ color: #606266;
+ display: inline-block;
+ line-height: 27px;
+ outline: none;
+ padding: 0 5px;
+}
+
+.menu-item .menu-item__search__collapse__search span {
+ height: 100%;
+ color: #dcdfe6;
+ font-size: 25px;
+ display: inline-block;
+ border: 0;
+}
+
.menu-item__print i {
background-image: url('./assets/images/print.svg');
}