feat:增加全局搜索

pr675
Hufe921 4 years ago
parent 3e11c52aaf
commit 765dc9da19

@ -59,6 +59,12 @@
<div class="menu-item__search">
<i></i>
</div>
<div class="menu-item__search__collapse">
<div class="menu-item__search__collapse__search">
<input type="text" />
<span>×</span>
</div>
</div>
<div class="menu-item__print">
<i></i>
</div>

@ -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()
}

@ -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())
}

@ -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<IEditorOption>, 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

@ -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<IEditorOption>
private draw: Draw
private position: Position
constructor(ctx: CanvasRenderingContext2D, options: Required<IEditorOption>, 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()
}
}

@ -9,7 +9,7 @@ export class RangeManager {
private options: Required<IEditorOption>
private range: IRange
constructor(ctx: CanvasRenderingContext2D, elementList: IElement[], options: Required<IEditorOption>,) {
constructor(ctx: CanvasRenderingContext2D, elementList: IElement[], options: Required<IEditorOption>) {
this.ctx = ctx
this.elementList = elementList
this.options = options

@ -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],

@ -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]

@ -70,6 +70,25 @@ window.onload = function () {
}
// 搜索、打印
const collspanDom = document.querySelector<HTMLDivElement>('.menu-item__search__collapse')
const searchInputDom = document.querySelector<HTMLInputElement>('.menu-item__search__collapse__search input')
document.querySelector<HTMLDivElement>('.menu-item__search')!.onclick = function () {
console.log('search')
collspanDom!.style.display = 'block'
}
document.querySelector<HTMLDivElement>('.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<HTMLDivElement>('.menu-item__print')!.onclick = function () {
console.log('print')
instance.command.executePrint()

@ -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');
}

Loading…
Cancel
Save