diff --git a/index.html b/index.html index 9925d65..d60a988 100644 --- a/index.html +++ b/index.html @@ -114,9 +114,7 @@ -
- -
+
diff --git a/src/editor/core/command/CommandAdapt.ts b/src/editor/core/command/CommandAdapt.ts index a96969c..23c013d 100644 --- a/src/editor/core/command/CommandAdapt.ts +++ b/src/editor/core/command/CommandAdapt.ts @@ -1,4 +1,5 @@ import { ZERO } from "../../dataset/constant/Common" +import { EDITOR_ELEMENT_STYLE } from "../../dataset/constant/Element" import { ElementType } from "../../dataset/enum/Element" import { ElementStyleKey } from "../../dataset/enum/ElementStyle" import { RowFlex } from "../../dataset/enum/Row" @@ -41,7 +42,7 @@ export class CommandAdapt { if (!selection) return const painterStyle: IElementStyle = {} selection.forEach(s => { - const painterStyleKeys = ['bold', 'color', 'highlight', 'font', 'size', 'italic', 'underline', 'strikeout'] + const painterStyleKeys = EDITOR_ELEMENT_STYLE painterStyleKeys.forEach(p => { const key = p as keyof typeof ElementStyleKey if (painterStyle[key] === undefined) { @@ -168,6 +169,7 @@ export class CommandAdapt { public rowFlex(payload: RowFlex) { const { startIndex, endIndex } = this.range.getRange() if (startIndex === 0 && endIndex === 0) return + const pageNo = this.draw.getPageNo() const positionList = this.position.getPositionList() // 开始/结束行号 const startRowNo = positionList[startIndex].rowNo @@ -176,6 +178,7 @@ export class CommandAdapt { // 当前选区所在行 for (let p = 0; p < positionList.length; p++) { const postion = positionList[p] + if (postion.pageNo !== pageNo) continue if (postion.rowNo > endRowNo) break if (postion.rowNo >= startRowNo && postion.rowNo <= endRowNo) { elementList[p].rowFlex = payload @@ -190,6 +193,7 @@ export class CommandAdapt { public rowMargin(payload: number) { const { startIndex, endIndex } = this.range.getRange() if (startIndex === 0 && endIndex === 0) return + const pageNo = this.draw.getPageNo() const positionList = this.position.getPositionList() // 开始/结束行号 const startRowNo = positionList[startIndex].rowNo @@ -198,6 +202,7 @@ export class CommandAdapt { // 当前选区所在行 for (let p = 0; p < positionList.length; p++) { const postion = positionList[p] + if (postion.pageNo !== pageNo) continue if (postion.rowNo > endRowNo) break if (postion.rowNo >= startRowNo && postion.rowNo <= endRowNo) { elementList[p].rowMargin = payload @@ -253,7 +258,8 @@ export class CommandAdapt { } public print() { - return printImageBase64(this.draw.getDataURL()) + const { width, height } = this.options + return printImageBase64(this.draw.getDataURL(), width, height) } } \ No newline at end of file diff --git a/src/editor/core/cursor/Cursor.ts b/src/editor/core/cursor/Cursor.ts index 8a7e07b..457fe6d 100644 --- a/src/editor/core/cursor/Cursor.ts +++ b/src/editor/core/cursor/Cursor.ts @@ -1,23 +1,27 @@ import { CURSOR_AGENT_HEIGHT } from "../../dataset/constant/Cursor" +import { IEditorOption } from "../../interface/Editor" import { Draw } from "../draw/Draw" import { CanvasEvent } from "../event/CanvasEvent" +import { Position } from "../position/Position" import { CursorAgent } from "./CursorAgent" export class Cursor { - private canvas: HTMLCanvasElement - private draw: Draw + private container: HTMLDivElement + private options: Required + private position: Position private cursorDom: HTMLDivElement private cursorAgent: CursorAgent - constructor(canvas: HTMLCanvasElement, draw: Draw, canvasEvent: CanvasEvent) { - this.canvas = canvas - this.draw = draw + constructor(draw: Draw, canvasEvent: CanvasEvent) { + this.container = draw.getContainer() + this.position = draw.getPosition() + this.options = draw.getOptions() this.cursorDom = document.createElement('div') this.cursorDom.classList.add('cursor') - this.canvas.parentNode?.append(this.cursorDom) - this.cursorAgent = new CursorAgent(canvas, canvasEvent) + this.container.append(this.cursorDom) + this.cursorAgent = new CursorAgent(draw, canvasEvent) } public getCursorDom(): HTMLDivElement { @@ -29,10 +33,12 @@ export class Cursor { } public drawCursor() { - const cursorPosition = this.draw.getPosition().getCursorPosition() + const cursorPosition = this.position.getCursorPosition() if (!cursorPosition) return // 设置光标代理 - const { metrics, coordinate: { leftTop, rightTop }, ascent } = cursorPosition + const { metrics, coordinate: { leftTop, rightTop }, ascent, pageNo } = cursorPosition + const { height, pageGap } = this.options + const preY = pageNo * (height + pageGap) // 增加1/4字体大小 const offsetHeight = metrics.height / 4 const cursorHeight = metrics.height + offsetHeight * 2 @@ -43,7 +49,7 @@ export class Cursor { }) // fillText位置 + 文字基线到底部距离 - 模拟光标偏移量 const descent = metrics.boundingBoxDescent < 0 ? 0 : metrics.boundingBoxDescent - const cursorTop = (leftTop[1] + ascent) + descent - (cursorHeight - offsetHeight) + const cursorTop = (leftTop[1] + ascent) + descent - (cursorHeight - offsetHeight) + preY const curosrleft = rightTop[0] agentCursorDom.style.left = `${curosrleft}px` agentCursorDom.style.top = `${cursorTop + cursorHeight - CURSOR_AGENT_HEIGHT}px` diff --git a/src/editor/core/cursor/CursorAgent.ts b/src/editor/core/cursor/CursorAgent.ts index 1c1d76f..4ac669e 100644 --- a/src/editor/core/cursor/CursorAgent.ts +++ b/src/editor/core/cursor/CursorAgent.ts @@ -1,21 +1,22 @@ import { debounce } from "../../utils" +import { Draw } from "../draw/Draw" import { CanvasEvent } from "../event/CanvasEvent" export class CursorAgent { - private canvas: HTMLCanvasElement + private container: HTMLDivElement private agentCursorDom: HTMLTextAreaElement private canvasEvent: CanvasEvent - constructor(canvas: HTMLCanvasElement, canvasEvent: CanvasEvent) { - this.canvas = canvas + constructor(draw: Draw, canvasEvent: CanvasEvent) { + this.container = draw.getContainer() this.canvasEvent = canvasEvent // 代理光标绘制 const agentCursorDom = document.createElement('textarea') agentCursorDom.autocomplete = 'off' agentCursorDom.classList.add('inputarea') agentCursorDom.innerText = '' - this.canvas.parentNode?.append(agentCursorDom) + this.container.append(agentCursorDom) this.agentCursorDom = agentCursorDom // 事件 agentCursorDom.onkeydown = (evt: KeyboardEvent) => this._keyDown(evt) diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts index f232c85..7af5d85 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -24,8 +24,11 @@ import { TextParticle } from "./particle/TextParticle" export class Draw { - private canvas: HTMLCanvasElement - private ctx: CanvasRenderingContext2D + private container: HTMLDivElement + private pageContainer: HTMLDivElement + private pageList: HTMLCanvasElement[] + private ctxList: CanvasRenderingContext2D[] + private pageNo: number private options: Required private position: Position private elementList: IElement[] @@ -48,41 +51,73 @@ export class Draw { private searchMatchList: number[][] | null constructor( - canvas: HTMLCanvasElement, - ctx: CanvasRenderingContext2D, + container: HTMLDivElement, options: Required, elementList: IElement[], listener: Listener ) { - this.canvas = canvas - this.ctx = ctx + this.container = container + this.pageList = [] + this.ctxList = [] + this.pageNo = 0 this.options = options this.elementList = elementList this.listener = listener + this.pageContainer = this._createPageContainer() + this._createPage(0) + this.historyManager = new HistoryManager() - this.position = new Position(options, this) - this.range = new RangeManager(ctx, options, this) - this.margin = new Margin(ctx, options) - this.background = new Background(ctx) - this.search = new Search(ctx, options, this) - this.underline = new Underline(ctx, options) - this.strikeout = new Strikeout(ctx, options) - this.highlight = new Highlight(ctx, options) - this.imageParticle = new ImageParticle(canvas, ctx, options, this) - this.textParticle = new TextParticle(ctx) - - const canvasEvent = new CanvasEvent(canvas, this) - this.cursor = new Cursor(canvas, this, canvasEvent) + this.position = new Position(this) + this.range = new RangeManager(this) + this.margin = new Margin(this) + this.background = new Background() + this.search = new Search(this) + this.underline = new Underline(this) + this.strikeout = new Strikeout(this) + this.highlight = new Highlight(this) + this.imageParticle = new ImageParticle(this) + this.textParticle = new TextParticle(this) + + const canvasEvent = new CanvasEvent(this) + this.cursor = new Cursor(this, canvasEvent) canvasEvent.register() - const globalEvent = new GlobalEvent(canvas, this, canvasEvent) + const globalEvent = new GlobalEvent(this, canvasEvent) globalEvent.register() this.rowList = [] this.painterStyle = null this.searchMatchList = null - this._setDefaultRange() + this.render({ isSetCursor: false }) + } + + public getContainer(): HTMLDivElement { + return this.container + } + + public getPageContainer(): HTMLDivElement { + return this.pageContainer + } + + public getPageNo(): number { + return this.pageNo + } + + public setPageNo(payload: number) { + this.pageNo = payload + } + + public getPage(): HTMLCanvasElement { + return this.pageList[this.pageNo] + } + + public getPageList(): HTMLCanvasElement[] { + return this.pageList + } + + public getCtx(): CanvasRenderingContext2D { + return this.ctxList[this.pageNo] } public getOptions(): Required { @@ -121,8 +156,8 @@ export class Draw { return this.rowList.length } - public getDataURL(): string { - return this.canvas.toDataURL() + public getDataURL(): string[] { + return this.pageList.map(c => c.toDataURL()) } public getPainterStyle(): IElementStyle | null { @@ -132,11 +167,11 @@ export class Draw { public setPainterStyle(payload: IElementStyle | null) { this.painterStyle = payload if (this.getPainterStyle()) { - this.canvas.style.cursor = 'copy' + this.pageList.forEach(c => c.style.cursor = 'copy') } } - public getSearchMathch(): number[][] | null { + public getSearchMatch(): number[][] | null { return this.searchMatchList } @@ -144,7 +179,7 @@ export class Draw { this.searchMatchList = payload } - private _setDefaultRange() { + public setDefaultRange() { if (!this.elementList.length) return setTimeout(() => { const curIndex = this.elementList.length - 1 @@ -153,15 +188,43 @@ export class Draw { }) } + private _createPageContainer(): HTMLDivElement { + // 容器宽度需跟随纸张宽度 + this.container.style.width = `${this.options.width}px` + const pageContainer = document.createElement('div') + pageContainer.classList.add('page-container') + this.container.append(pageContainer) + return pageContainer + } + + private _createPage(pageNo: number) { + const canvas = document.createElement('canvas') + canvas.style.width = `${this.options.width}px` + canvas.style.height = `${this.options.height}px` + canvas.style.marginBottom = `${this.options.pageGap}px` + canvas.setAttribute('data-index', String(pageNo)) + this.pageContainer.append(canvas) + // 调整分辨率 + const dpr = window.devicePixelRatio + canvas.width = parseInt(canvas.style.width) * dpr + canvas.height = parseInt(canvas.style.height) * dpr + canvas.style.cursor = 'text' + const ctx = canvas.getContext('2d')! + ctx.scale(dpr, dpr) + // 缓存上下文 + this.pageList.push(canvas) + this.ctxList.push(ctx) + } + private _getFont(el: IElement): string { const { defaultSize, defaultFont } = this.options return `${el.italic ? 'italic ' : ''}${el.bold ? 'bold ' : ''}${el.size || defaultSize}px ${el.font || defaultFont}` } private _computeRowList() { - const { defaultSize } = this.options - const canvasRect = this.canvas.getBoundingClientRect() - const { width } = canvasRect + const { defaultSize, width } = this.options + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') as CanvasRenderingContext2D const { margins, defaultRowMargin, defaultBasicRowMarginHeight } = this.options const leftTopPoint: [number, number] = [margins[3], margins[0]] const rightTopPoint: [number, number] = [width - margins[1], margins[0]] @@ -176,7 +239,6 @@ export class Draw { rowFlex: this.elementList?.[1]?.rowFlex }) } - this.ctx.save() for (let i = 0; i < this.elementList.length; i++) { const curRow: IRow = rowList[rowList.length - 1] const element = this.elementList[i] @@ -201,8 +263,8 @@ export class Draw { metrics.boundingBoxDescent = element.height! } else { metrics.height = element.size || this.options.defaultSize - this.ctx.font = this._getFont(element) - const fontMetrics = this.ctx.measureText(element.value) + ctx.font = this._getFont(element) + const fontMetrics = ctx.measureText(element.value) metrics.width = fontMetrics.width metrics.boundingBoxAscent = element.value === ZERO ? defaultSize : fontMetrics.actualBoundingBoxAscent metrics.boundingBoxDescent = fontMetrics.actualBoundingBoxDescent @@ -213,7 +275,7 @@ export class Draw { const rowElement: IRowElement = { ...element, metrics, - style: this.ctx.font + style: ctx.font } // 超过限定宽度 if (curRow.width + metrics.width > innerWidth || (i !== 0 && element.value === ZERO)) { @@ -237,50 +299,30 @@ export class Draw { curRow.elementList.push(rowElement) } } - this.ctx.restore() this.rowList = rowList } - public render(payload?: IDrawOption) { - let { - curIndex, - isSubmitHistory = true, - isSetCursor = true, - isComputeRowList = true - } = payload || {} - // 计算行信息 + private _drawElement(positionList: IElementPosition[], rowList: IRow[], pageNo: number) { const { margins } = this.options - if (isComputeRowList) { - this._computeRowList() - // 计算高度是否超出 - const rowHeight = this.rowList.reduce((pre, cur) => cur.height + pre, 0) - if (rowHeight > this.canvas.height - margins[0] - margins[2]) { - const height = Math.ceil(rowHeight + margins[0] + margins[2]) - this.canvas.height = height - this.canvas.style.height = `${height}px` - } - } - // 清除光标等副作用 - this.cursor.recoveryCursor() - this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height) - this.position.setPositionList([]) - const positionList = this.position.getPositionList() + const canvas = this.pageList[pageNo] + const ctx = this.ctxList[pageNo] + ctx.clearRect(0, 0, canvas.width, canvas.height) // 基础信息 - const canvasRect = this.canvas.getBoundingClientRect() + const canvasRect = canvas.getBoundingClientRect() // 绘制背景 - this.background.render(canvasRect) + this.background.render(ctx, canvasRect) // 绘制页边距 const leftTopPoint: [number, number] = [margins[3], margins[0]] - this.margin.render(canvasRect) + this.margin.render(ctx, canvasRect) // 渲染元素 let x = leftTopPoint[0] let y = leftTopPoint[1] - let index = 0 - for (let i = 0; i < this.rowList.length; i++) { - const curRow = this.rowList[i] + let index = positionList.length + for (let i = 0; i < rowList.length; i++) { + const curRow = rowList[i] // 计算行偏移量(行居左、居中、居右) if (curRow.rowFlex && curRow.rowFlex !== RowFlex.LEFT) { - const canvasInnerWidth = this.canvas.width - margins[1] - margins[3] + const canvasInnerWidth = canvas.width - margins[1] - margins[3] if (curRow.rowFlex === RowFlex.CENTER) { x += (canvasInnerWidth - curRow.width) / 2 } else { @@ -294,6 +336,7 @@ export class Draw { ? curRow.ascent - element.height! : curRow.ascent const positionItem: IElementPosition = { + pageNo, index, value: element.value, rowNo: i, @@ -311,27 +354,27 @@ export class Draw { positionList.push(positionItem) // 下划线绘制 if (element.underline) { - this.underline.render(x, y + curRow.height, metrics.width) + this.underline.render(ctx, x, y + curRow.height, metrics.width) } // 删除线绘制 if (element.strikeout) { - this.strikeout.render(x, y + curRow.height / 2, metrics.width) + this.strikeout.render(ctx, x, y + curRow.height / 2, metrics.width) } // 元素高亮 if (element.highlight) { - this.highlight.render(element.highlight, x, y, metrics.width, curRow.height) + this.highlight.render(ctx, element.highlight, x, y, metrics.width, curRow.height) } // 元素绘制 if (element.type === ElementType.IMAGE) { this.textParticle.complete() - this.imageParticle.render(element, x, y + offsetY) + this.imageParticle.render(ctx, element, x, y + offsetY) } else { - this.textParticle.record(element, x, y + offsetY) + this.textParticle.record(ctx, element, x, y + offsetY) } // 选区绘制 const { startIndex, endIndex } = this.range.getRange() if (startIndex !== endIndex && startIndex < index && index <= endIndex) { - this.range.render(x, y, metrics.width, curRow.height) + this.range.render(ctx, x, y, metrics.width, curRow.height) } index++ x += metrics.width @@ -342,13 +385,66 @@ export class Draw { } // 搜索匹配绘制 if (this.searchMatchList) { - this.search.render() + this.search.render(ctx, pageNo) } - // 光标重绘 - if (curIndex === undefined) { - curIndex = positionList.length - 1 + } + + public render(payload?: IDrawOption) { + let { + curIndex, + isSubmitHistory = true, + isSetCursor = true, + isComputeRowList = true + } = payload || {} + // 计算行信息 + if (isComputeRowList) { + this._computeRowList() } + // 清除光标等副作用 + this.cursor.recoveryCursor() + this.position.setPositionList([]) + const positionList = this.position.getPositionList() + // 按页渲染 + const { margins } = this.options + const marginHeight = margins[0] + margins[2] + let pageHeight = marginHeight + let pageNo = 0 + let pageRowList: IRow[][] = [[]] + for (let i = 0; i < this.rowList.length; i++) { + const row = this.rowList[i] + if (row.height + pageHeight > this.options.height) { + pageHeight = marginHeight + row.height + pageRowList.push([row]) + pageNo++ + } else { + pageHeight += row.height + pageRowList[pageNo].push(row) + } + } + // 绘制元素 + for (let i = 0; i < pageRowList.length; i++) { + if (!this.pageList[i]) { + this._createPage(i) + } + const rowList = pageRowList[i] + this._drawElement(positionList, rowList, i) + } + // 移除多余页 + setTimeout(() => { + const curPageCount = pageRowList.length + const prePageCount = this.pageList.length + if (prePageCount > curPageCount) { + const deleteCount = prePageCount - curPageCount + this.ctxList.splice(curPageCount, deleteCount) + this.pageList.splice(curPageCount, deleteCount) + .forEach(page => page.remove()) + } + }) + // 光标重绘 if (isSetCursor) { + if (curIndex === undefined) { + curIndex = positionList.length - 1 + } this.position.setCursorPosition(positionList[curIndex!] || null) this.cursor.drawCursor() } @@ -357,7 +453,9 @@ export class Draw { const self = this const oldElementList = deepClone(this.elementList) const { startIndex, endIndex } = this.range.getRange() + const pageNo = this.pageNo this.historyManager.execute(function () { + self.setPageNo(pageNo) self.range.setRange(startIndex, endIndex) self.elementList = deepClone(oldElementList) self.render({ curIndex, isSubmitHistory: false }) diff --git a/src/editor/core/draw/frame/Background.ts b/src/editor/core/draw/frame/Background.ts index 34503d0..ad2212f 100644 --- a/src/editor/core/draw/frame/Background.ts +++ b/src/editor/core/draw/frame/Background.ts @@ -1,17 +1,11 @@ export class Background { - private ctx: CanvasRenderingContext2D - - constructor(ctx: CanvasRenderingContext2D) { - this.ctx = ctx - } - - public render(canvasRect: DOMRect) { + public render(ctx: CanvasRenderingContext2D, canvasRect: DOMRect) { const { width, height } = canvasRect - this.ctx.save() - this.ctx.fillStyle = '#ffffff' - this.ctx.fillRect(0, 0, width, height) - this.ctx.restore() + ctx.save() + ctx.fillStyle = '#ffffff' + ctx.fillRect(0, 0, width, height) + ctx.restore() } } \ No newline at end of file diff --git a/src/editor/core/draw/frame/Margin.ts b/src/editor/core/draw/frame/Margin.ts index b65d81f..a50fe20 100644 --- a/src/editor/core/draw/frame/Margin.ts +++ b/src/editor/core/draw/frame/Margin.ts @@ -1,43 +1,42 @@ import { IEditorOption } from "../../../interface/Editor" +import { Draw } from "../Draw" export class Margin { - private ctx: CanvasRenderingContext2D private options: Required - constructor(ctx: CanvasRenderingContext2D, options: Required) { - this.ctx = ctx - this.options = options + constructor(draw: Draw) { + this.options = draw.getOptions() } - public render(canvasRect: DOMRect) { + public render(ctx: CanvasRenderingContext2D, canvasRect: DOMRect) { const { width, height } = canvasRect const { marginIndicatorColor, marginIndicatorSize, margins } = this.options - this.ctx.save() - this.ctx.strokeStyle = marginIndicatorColor - this.ctx.beginPath() + ctx.save() + ctx.strokeStyle = marginIndicatorColor + ctx.beginPath() const leftTopPoint: [number, number] = [margins[3], margins[0]] const rightTopPoint: [number, number] = [width - margins[1], margins[0]] const leftBottomPoint: [number, number] = [margins[3], height - margins[2]] const rightBottomPoint: [number, number] = [width - margins[1], height - margins[2]] // 上左 - this.ctx.moveTo(leftTopPoint[0] - marginIndicatorSize, leftTopPoint[1]) - this.ctx.lineTo(...leftTopPoint) - this.ctx.lineTo(leftTopPoint[0], leftTopPoint[1] - marginIndicatorSize) + ctx.moveTo(leftTopPoint[0] - marginIndicatorSize, leftTopPoint[1]) + ctx.lineTo(...leftTopPoint) + ctx.lineTo(leftTopPoint[0], leftTopPoint[1] - marginIndicatorSize) // 上右 - this.ctx.moveTo(rightTopPoint[0] + marginIndicatorSize, rightTopPoint[1]) - this.ctx.lineTo(...rightTopPoint) - this.ctx.lineTo(rightTopPoint[0], rightTopPoint[1] - marginIndicatorSize) + ctx.moveTo(rightTopPoint[0] + marginIndicatorSize, rightTopPoint[1]) + ctx.lineTo(...rightTopPoint) + ctx.lineTo(rightTopPoint[0], rightTopPoint[1] - marginIndicatorSize) // 下左 - this.ctx.moveTo(leftBottomPoint[0] - marginIndicatorSize, leftBottomPoint[1]) - this.ctx.lineTo(...leftBottomPoint) - this.ctx.lineTo(leftBottomPoint[0], leftBottomPoint[1] + marginIndicatorSize) + ctx.moveTo(leftBottomPoint[0] - marginIndicatorSize, leftBottomPoint[1]) + ctx.lineTo(...leftBottomPoint) + ctx.lineTo(leftBottomPoint[0], leftBottomPoint[1] + marginIndicatorSize) // 下右 - this.ctx.moveTo(rightBottomPoint[0] + marginIndicatorSize, rightBottomPoint[1]) - this.ctx.lineTo(...rightBottomPoint) - this.ctx.lineTo(rightBottomPoint[0], rightBottomPoint[1] + marginIndicatorSize) - this.ctx.stroke() - this.ctx.restore() + ctx.moveTo(rightBottomPoint[0] + marginIndicatorSize, rightBottomPoint[1]) + ctx.lineTo(...rightBottomPoint) + ctx.lineTo(rightBottomPoint[0], rightBottomPoint[1] + marginIndicatorSize) + ctx.stroke() + ctx.restore() } } \ No newline at end of file diff --git a/src/editor/core/draw/interactive/Search.ts b/src/editor/core/draw/interactive/Search.ts index d50bb64..e0c8c04 100644 --- a/src/editor/core/draw/interactive/Search.ts +++ b/src/editor/core/draw/interactive/Search.ts @@ -4,36 +4,36 @@ import { Draw } from "../Draw" export class Search { - private ctx: CanvasRenderingContext2D - private options: Required private draw: Draw + private options: Required private position: Position - constructor(ctx: CanvasRenderingContext2D, options: Required, draw: Draw) { - this.ctx = ctx - this.options = options + constructor(draw: Draw) { this.draw = draw + this.options = draw.getOptions() this.position = draw.getPosition() } - public render() { - const searchMatch = this.draw.getSearchMathch() + public render(ctx: CanvasRenderingContext2D, pageIndex: number) { + const searchMatch = this.draw.getSearchMatch() 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 + ctx.save() + ctx.globalAlpha = this.options.searchMatchAlpha + ctx.fillStyle = this.options.searchMatchColor searchMatchList.forEach(s => { const position = positionList[s] - const { leftTop, leftBottom, rightTop } = position.coordinate + if (!position) return + const { coordinate: { leftTop, leftBottom, rightTop }, pageNo } = position + if (pageNo !== pageIndex) return 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) + ctx.fillRect(x, y, width, height) }) - this.ctx.restore() + ctx.restore() } } \ No newline at end of file diff --git a/src/editor/core/draw/particle/ImageParticle.ts b/src/editor/core/draw/particle/ImageParticle.ts index 953b840..39f939b 100644 --- a/src/editor/core/draw/particle/ImageParticle.ts +++ b/src/editor/core/draw/particle/ImageParticle.ts @@ -5,8 +5,8 @@ import { Draw } from "../Draw" export class ImageParticle { + private container: HTMLDivElement private canvas: HTMLCanvasElement - private ctx: CanvasRenderingContext2D private draw: Draw private options: Required private curElement: IElement | null @@ -22,11 +22,11 @@ export class ImageParticle { private mousedownY: number private curHandleIndex: number - constructor(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, options: Required, draw: Draw) { - this.canvas = canvas - this.ctx = ctx + constructor(draw: Draw) { + this.container = draw.getContainer() + this.canvas = draw.getPage() this.draw = draw - this.options = options + this.options = draw.getOptions() this.curElement = null this.curPosition = null this.imageCache = new Map() @@ -58,19 +58,21 @@ export class ImageParticle { resizerSelection.append(handleDom) resizerHandleList.push(handleDom) } - this.canvas.parentNode!.append(resizerSelection) + this.container.append(resizerSelection) // 拖拽镜像 const resizerImageContainer = document.createElement('div') resizerImageContainer.classList.add('resizer-image') resizerImageContainer.style.display = 'none' const resizerImage = document.createElement('img') resizerImageContainer.append(resizerImage) - this.canvas.parentNode!.append(resizerImageContainer) + this.container.append(resizerImageContainer) return { resizerSelection, resizerHandleList, resizerImageContainer, resizerImage } } private _handleMousedown(evt: MouseEvent) { + this.canvas = this.draw.getPage() if (!this.curPosition || !this.curElement) return + const { height, pageGap } = this.options this.mousedownX = evt.x this.mousedownY = evt.y const target = evt.target as HTMLDivElement @@ -83,8 +85,9 @@ export class ImageParticle { this.resizerImage.src = this.curElement?.value! this.resizerImageContainer.style.display = 'block' const { coordinate: { leftTop: [left, top] } } = this.curPosition + const prePageHeight = this.draw.getPageNo() * (height + pageGap) this.resizerImageContainer.style.left = `${left}px` - this.resizerImageContainer.style.top = `${top}px` + this.resizerImageContainer.style.top = `${top + prePageHeight}px` this.resizerImage.style.width = `${this.curElement.width}px` this.resizerImage.style.height = `${this.curElement.height}px` // 追加全局事件 @@ -159,9 +162,10 @@ export class ImageParticle { const width = element.width! const height = element.height! const handleSize = this.options.resizerSize + const preY = this.draw.getPageNo() * (this.options.height + this.options.pageGap) // 边框 this.resizerSelection.style.left = `${left}px` - this.resizerSelection.style.top = `${top}px` + this.resizerSelection.style.top = `${top + preY}px` this.resizerSelection.style.width = `${element.width}px` this.resizerSelection.style.height = `${element.height}px` // handle @@ -190,17 +194,17 @@ export class ImageParticle { this.resizerSelection.style.display = 'none' } - public render(element: IElement, x: number, y: number) { + public render(ctx: CanvasRenderingContext2D, element: IElement, x: number, y: number) { const width = element.width! const height = element.height! if (this.imageCache.has(element.id!)) { const img = this.imageCache.get(element.id!)! - this.ctx.drawImage(img, x, y, width, height) + ctx.drawImage(img, x, y, width, height) } else { const img = new Image() img.src = element.value img.onload = () => { - this.ctx.drawImage(img, x, y, width, height) + ctx.drawImage(img, x, y, width, height) this.imageCache.set(element.id!, img) } } diff --git a/src/editor/core/draw/particle/TextParticle.ts b/src/editor/core/draw/particle/TextParticle.ts index d9e12ba..753a1cc 100644 --- a/src/editor/core/draw/particle/TextParticle.ts +++ b/src/editor/core/draw/particle/TextParticle.ts @@ -1,4 +1,5 @@ import { IRowElement } from "../../../interface/Row" +import { Draw } from "../Draw" export class TextParticle { @@ -9,8 +10,8 @@ export class TextParticle { private curStyle: string private curColor?: string - constructor(ctx: CanvasRenderingContext2D) { - this.ctx = ctx + constructor(draw: Draw) { + this.ctx = draw.getCtx() this.curX = -1 this.curY = -1 this.text = '' @@ -22,7 +23,8 @@ export class TextParticle { this.text = '' } - public record(element: IRowElement, x: number, y: number) { + public record(ctx: CanvasRenderingContext2D, element: IRowElement, x: number, y: number) { + this.ctx = ctx // 主动完成的重设起始点 if (!this.text) { this._setCurXY(x, y) diff --git a/src/editor/core/draw/richtext/Highlight.ts b/src/editor/core/draw/richtext/Highlight.ts index 89912fe..c934d58 100644 --- a/src/editor/core/draw/richtext/Highlight.ts +++ b/src/editor/core/draw/richtext/Highlight.ts @@ -1,22 +1,21 @@ import { IEditorOption } from "../../../interface/Editor" +import { Draw } from "../Draw" export class Highlight { - private ctx: CanvasRenderingContext2D private options: Required - constructor(ctx: CanvasRenderingContext2D, options: Required) { - this.ctx = ctx - this.options = options + constructor(draw: Draw) { + this.options = draw.getOptions() } - public render(color: string, x: number, y: number, width: number, height: number) { + public render(ctx: CanvasRenderingContext2D, color: string, x: number, y: number, width: number, height: number) { const { highlightAlpha } = this.options - this.ctx.save() - this.ctx.globalAlpha = highlightAlpha - this.ctx.fillStyle = color - this.ctx.fillRect(x, y, width, height) - this.ctx.restore() + ctx.save() + ctx.globalAlpha = highlightAlpha + ctx.fillStyle = color + ctx.fillRect(x, y, width, height) + ctx.restore() } } \ No newline at end of file diff --git a/src/editor/core/draw/richtext/Strikeout.ts b/src/editor/core/draw/richtext/Strikeout.ts index b22f82c..91b1cb5 100644 --- a/src/editor/core/draw/richtext/Strikeout.ts +++ b/src/editor/core/draw/richtext/Strikeout.ts @@ -1,24 +1,23 @@ import { IEditorOption } from "../../../interface/Editor" +import { Draw } from "../Draw" export class Strikeout { - private ctx: CanvasRenderingContext2D private options: Required - constructor(ctx: CanvasRenderingContext2D, options: Required) { - this.ctx = ctx - this.options = options + constructor(draw: Draw) { + this.options = draw.getOptions() } - public render(x: number, y: number, width: number) { + public render(ctx: CanvasRenderingContext2D, x: number, y: number, width: number) { const { strikeoutColor } = this.options - this.ctx.save() - this.ctx.strokeStyle = strikeoutColor - this.ctx.beginPath() - this.ctx.moveTo(x, y) - this.ctx.lineTo(x + width, y) - this.ctx.stroke() - this.ctx.restore() + ctx.save() + ctx.strokeStyle = strikeoutColor + ctx.beginPath() + ctx.moveTo(x, y) + ctx.lineTo(x + width, y) + ctx.stroke() + ctx.restore() } } \ No newline at end of file diff --git a/src/editor/core/draw/richtext/Underline.ts b/src/editor/core/draw/richtext/Underline.ts index da58480..aed327d 100644 --- a/src/editor/core/draw/richtext/Underline.ts +++ b/src/editor/core/draw/richtext/Underline.ts @@ -1,24 +1,23 @@ import { IEditorOption } from "../../../interface/Editor" +import { Draw } from "../Draw" export class Underline { - private ctx: CanvasRenderingContext2D private options: Required - constructor(ctx: CanvasRenderingContext2D, options: Required) { - this.ctx = ctx - this.options = options + constructor(draw: Draw) { + this.options = draw.getOptions() } - public render(x: number, y: number, width: number) { + public render(ctx: CanvasRenderingContext2D, x: number, y: number, width: number) { const { underlineColor } = this.options - this.ctx.save() - this.ctx.strokeStyle = underlineColor - this.ctx.beginPath() - this.ctx.moveTo(x, y) - this.ctx.lineTo(x + width, y) - this.ctx.stroke() - this.ctx.restore() + ctx.save() + ctx.strokeStyle = underlineColor + ctx.beginPath() + ctx.moveTo(x, y) + ctx.lineTo(x + width, y) + ctx.stroke() + ctx.restore() } } \ No newline at end of file diff --git a/src/editor/core/event/CanvasEvent.ts b/src/editor/core/event/CanvasEvent.ts index edc8a58..3265492 100644 --- a/src/editor/core/event/CanvasEvent.ts +++ b/src/editor/core/event/CanvasEvent.ts @@ -17,19 +17,21 @@ export class CanvasEvent { private mouseDownStartIndex: number private draw: Draw - private canvas: HTMLCanvasElement + private pageContainer: HTMLDivElement + private pageList: HTMLCanvasElement[] private position: Position private range: RangeManager private cursor: Cursor | null private historyManager: HistoryManager private imageParticle: ImageParticle - constructor(canvas: HTMLCanvasElement, draw: Draw) { + constructor(draw: Draw) { this.isAllowDrag = false this.isCompositing = false this.mouseDownStartIndex = 0 - this.canvas = canvas + this.pageContainer = draw.getPageContainer() + this.pageList = draw.getPageList() this.draw = draw this.cursor = null this.position = this.draw.getPosition() @@ -41,15 +43,17 @@ export class CanvasEvent { public register() { // 延迟加载 this.cursor = this.draw.getCursor() - this.canvas.addEventListener('mousedown', this.mousedown.bind(this)) - this.canvas.addEventListener('mouseleave', this.mouseleave.bind(this)) - this.canvas.addEventListener('mousemove', this.mousemove.bind(this)) + this.pageContainer.addEventListener('mousedown', this.mousedown.bind(this)) + this.pageContainer.addEventListener('mouseleave', this.mouseleave.bind(this)) + this.pageContainer.addEventListener('mousemove', this.mousemove.bind(this)) } public setIsAllowDrag(payload: boolean) { this.isAllowDrag = payload if (payload === false) { - this.canvas.style.cursor = 'text' + this.pageList.forEach(p => { + p.style.cursor = 'text' + }) // 应用格式刷样式 const painterStyle = this.draw.getPainterStyle() if (!painterStyle) return @@ -69,15 +73,21 @@ export class CanvasEvent { public mousemove(evt: MouseEvent) { if (!this.isAllowDrag) return + const target = evt.target as HTMLDivElement + const pageIndex = target.dataset.index + // 设置pageNo + if (pageIndex) { + this.draw.setPageNo(Number(pageIndex)) + } // 结束位置 - const { index: endIndex } = this.draw.getPosition().getPositionByXY(evt.offsetX, evt.offsetY) + const { index: endIndex } = this.position.getPositionByXY(evt.offsetX, evt.offsetY) let end = ~endIndex ? endIndex : 0 // 开始位置 let start = this.mouseDownStartIndex if (start > end) { [start, end] = [end, start] } - this.draw.getRange().setRange(start, end) + this.range.setRange(start, end) if (start === end) return // 绘制 this.draw.render({ @@ -88,8 +98,14 @@ export class CanvasEvent { } public mousedown(evt: MouseEvent) { + const target = evt.target as HTMLDivElement + const pageIndex = target.dataset.index + // 设置pageNo + if (pageIndex) { + this.draw.setPageNo(Number(pageIndex)) + } this.isAllowDrag = true - const { index, isDirectHit, isImage } = this.draw.getPosition().getPositionByXY(evt.offsetX, evt.offsetY) + const { index, isDirectHit, isImage } = this.position.getPositionByXY(evt.offsetX, evt.offsetY) // 记录选区开始位置 this.mouseDownStartIndex = index // 绘制 @@ -114,7 +130,7 @@ export class CanvasEvent { public mouseleave(evt: MouseEvent) { // 是否还在canvas内部 - const { x, y, width, height } = this.canvas.getBoundingClientRect() + const { x, y, width, height } = this.pageContainer.getBoundingClientRect() if (evt.x >= x && evt.x <= x + width && evt.y >= y && evt.y <= y + height) return this.setIsAllowDrag(false) } @@ -141,6 +157,15 @@ export class CanvasEvent { const curIndex = isCollspace ? index - 1 : startIndex this.range.setRange(curIndex, curIndex) this.draw.render({ curIndex }) + } else if (evt.key === KeyMap.Delete) { + if (!isCollspace) { + elementList.splice(startIndex + 1, endIndex - startIndex) + } else { + elementList.splice(index + 1, 1) + } + const curIndex = isCollspace ? index : startIndex + this.range.setRange(curIndex, curIndex) + this.draw.render({ curIndex }) } else if (evt.key === KeyMap.Enter) { const enterText: IElement = { value: ZERO diff --git a/src/editor/core/event/GlobalEvent.ts b/src/editor/core/event/GlobalEvent.ts index 8dce009..4752406 100644 --- a/src/editor/core/event/GlobalEvent.ts +++ b/src/editor/core/event/GlobalEvent.ts @@ -8,16 +8,16 @@ import { CanvasEvent } from "./CanvasEvent" export class GlobalEvent { - private canvas: HTMLCanvasElement private draw: Draw + private canvas: HTMLCanvasElement private cursor: Cursor | null private canvasEvent: CanvasEvent private range: RangeManager private imageParticle: ImageParticle - constructor(canvas: HTMLCanvasElement, draw: Draw, canvasEvent: CanvasEvent) { - this.canvas = canvas + constructor(draw: Draw, canvasEvent: CanvasEvent) { this.draw = draw + this.canvas = draw.getPage() this.canvasEvent = canvasEvent this.cursor = null this.range = draw.getRange() diff --git a/src/editor/core/position/Position.ts b/src/editor/core/position/Position.ts index 6a77981..ad39db6 100644 --- a/src/editor/core/position/Position.ts +++ b/src/editor/core/position/Position.ts @@ -14,13 +14,13 @@ export class Position { private draw: Draw private options: Required - constructor(options: Required, draw: Draw) { + constructor(draw: Draw) { this.positionList = [] this.elementList = [] this.cursorPosition = null - this.options = options this.draw = draw + this.options = draw.getOptions() } public getPositionList(): IElementPosition[] { @@ -41,8 +41,10 @@ export class Position { public getPositionByXY(x: number, y: number): ICurrentPosition { this.elementList = this.draw.getElementList() + const curPageNo = this.draw.getPageNo() for (let j = 0; j < this.positionList.length; j++) { - const { index, coordinate: { leftTop, rightTop, leftBottom } } = this.positionList[j]; + const { index, pageNo, coordinate: { leftTop, rightTop, leftBottom } } = this.positionList[j] + if (curPageNo !== pageNo) continue // 命中元素 if (leftTop[0] <= x && rightTop[0] >= x && leftTop[1] <= y && leftBottom[1] >= y) { let curPostionIndex = j @@ -65,9 +67,10 @@ export class Position { let isLastArea = false let curPostionIndex = -1 // 判断所属行是否存在元素 - const firstLetterList = this.positionList.filter(p => p.isLastLetter) + const firstLetterList = this.positionList.filter(p => p.isLastLetter && p.pageNo === curPageNo) for (let j = 0; j < firstLetterList.length; j++) { - const { index, coordinate: { leftTop, leftBottom } } = firstLetterList[j] + const { index, pageNo, coordinate: { leftTop, leftBottom } } = firstLetterList[j] + if (curPageNo !== pageNo) continue if (y > leftTop[1] && y <= leftBottom[1]) { const isHead = x < this.options.margins[3] // 是否在头部 @@ -82,7 +85,8 @@ export class Position { } } if (!isLastArea) { - return { index: this.positionList.length - 1 } + // 当前页最后一行 + return { index: firstLetterList[firstLetterList.length - 1]?.index || this.positionList.length - 1 } } return { index: curPostionIndex } } diff --git a/src/editor/core/range/RangeManager.ts b/src/editor/core/range/RangeManager.ts index ece1c04..f0e1af0 100644 --- a/src/editor/core/range/RangeManager.ts +++ b/src/editor/core/range/RangeManager.ts @@ -7,17 +7,15 @@ import { Listener } from "../listener/Listener" export class RangeManager { - private ctx: CanvasRenderingContext2D + private draw: Draw 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 + constructor(draw: Draw) { this.draw = draw + this.options = draw.getOptions() this.listener = draw.getListener() this.historyManager = draw.getHistoryManager() this.range = { @@ -104,12 +102,12 @@ export class RangeManager { }) } - 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() + public render(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number) { + ctx.save() + ctx.globalAlpha = this.options.rangeAlpha + ctx.fillStyle = this.options.rangeColor + ctx.fillRect(x, y, width, height) + ctx.restore() } } \ No newline at end of file diff --git a/src/editor/dataset/constant/Element.ts b/src/editor/dataset/constant/Element.ts new file mode 100644 index 0000000..305820c --- /dev/null +++ b/src/editor/dataset/constant/Element.ts @@ -0,0 +1 @@ +export const EDITOR_ELEMENT_STYLE = ['bold', 'color', 'highlight', 'font', 'size', 'italic', 'underline', 'strikeout'] \ No newline at end of file diff --git a/src/editor/dataset/enum/Keymap.ts b/src/editor/dataset/enum/Keymap.ts index a293df2..3db5839 100644 --- a/src/editor/dataset/enum/Keymap.ts +++ b/src/editor/dataset/enum/Keymap.ts @@ -1,4 +1,5 @@ export enum KeyMap { + Delete = 'Delete', Backspace = 'Backspace', Enter = "Enter", Left = "ArrowLeft", diff --git a/src/editor/index.ts b/src/editor/index.ts index 545fdf1..39f14f3 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -15,13 +15,16 @@ export default class Editor { public command: Command public listener: Listener - constructor(canvas: HTMLCanvasElement, elementList: IElement[], options: IEditorOption = {}) { + constructor(container: HTMLDivElement, elementList: IElement[], options: IEditorOption = {}) { const editorOptions: Required = { defaultType: 'TEXT', defaultFont: 'Yahei', defaultSize: 16, defaultRowMargin: 1, defaultBasicRowMarginHeight: 8, + width: 794, + height: 1123, + pageGap: 20, underlineColor: '#000000', strikeoutColor: '#FF0000', rangeAlpha: 0.6, @@ -36,12 +39,6 @@ export default class Editor { margins: [100, 120, 100, 120], ...options } - const ctx = canvas.getContext('2d') as CanvasRenderingContext2D - const dpr = window.devicePixelRatio - canvas.width = parseInt(canvas.style.width) * dpr - canvas.height = parseInt(canvas.style.height) * dpr - canvas.style.cursor = 'text' - ctx.scale(dpr, dpr) if (elementList[0]?.value !== ZERO) { elementList.unshift({ value: ZERO @@ -59,8 +56,7 @@ export default class Editor { // 监听 this.listener = new Listener() // 启动 - const draw = new Draw(canvas, ctx, editorOptions, elementList, this.listener) - draw.render() + const draw = new Draw(container, editorOptions, elementList, this.listener) // 命令 this.command = new Command(new CommandAdapt(draw)) } diff --git a/src/editor/interface/Editor.ts b/src/editor/interface/Editor.ts index 8afaa27..dc53850 100644 --- a/src/editor/interface/Editor.ts +++ b/src/editor/interface/Editor.ts @@ -4,6 +4,9 @@ export interface IEditorOption { defaultSize?: number; defaultBasicRowMarginHeight?: number; defaultRowMargin?: number; + width?: number; + height?: number; + pageGap?: number; underlineColor?: string; strikeoutColor?: string; rangeColor?: string; diff --git a/src/editor/interface/Element.ts b/src/editor/interface/Element.ts index 614c026..89c607e 100644 --- a/src/editor/interface/Element.ts +++ b/src/editor/interface/Element.ts @@ -32,6 +32,7 @@ export interface IElementBasic { export type IElement = IElementBasic & IElementStyle export interface IElementPosition { + pageNo: number; index: number; value: string, rowNo: number; diff --git a/src/editor/interface/Listener.ts b/src/editor/interface/Listener.ts index 4bf5502..2f5dc19 100644 --- a/src/editor/interface/Listener.ts +++ b/src/editor/interface/Listener.ts @@ -15,4 +15,4 @@ export interface IRangeStype { rowMargin: number } -export type IRangeStyleChange = (payload: IRangeStype) => void; +export type IRangeStyleChange = (payload: IRangeStype) => void diff --git a/src/editor/utils/print.ts b/src/editor/utils/print.ts index 76b9bad..d9f49ad 100644 --- a/src/editor/utils/print.ts +++ b/src/editor/utils/print.ts @@ -1,17 +1,21 @@ -export function printImageBase64(base64: string) { +export function printImageBase64(base64List: string[], width: number, height: number) { const iframe = document.createElement('iframe') document.body.append(iframe) const doc = iframe.contentWindow!.document doc.open() - const image = doc.createElement('img') - image.style.width = '794px' - image.style.height = '1123px' - image.src = base64 + const container = document.createElement('div') + base64List.forEach(base64 => { + const image = document.createElement('img') + image.style.width = `${width}px` + image.style.height = `${height}px` + image.src = base64 + container.append(image) + }) const style = document.createElement('style') const stylesheet = `*{margin:0;padding:0;}` style.append(document.createTextNode(stylesheet)) setTimeout(() => { - doc.write(`${style.outerHTML}${image.outerHTML}`) + doc.write(`${style.outerHTML}${container.innerHTML}`) iframe.contentWindow?.print() doc.close() iframe.remove() diff --git a/src/main.ts b/src/main.ts index 7c99e8d..099d1cc 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,9 +3,7 @@ import Editor, { ElementType, IElement, RowFlex } from './editor' window.onload = function () { - const canvas = document.querySelector('canvas') - if (!canvas) return - const text = `人民医院门诊病历\n主诉:\n发热三天,咳嗽五天。\n现病史:\n发病前14天内有病历报告社区的旅行时或居住史;发病前14天内与新型冠状病毒感染的患者或无症状感染者有接触史;发病前14天内解除过来自病历报告社区的发热或有呼吸道症状的患者;聚集性发病,2周内在小范围如家庭、办公室、学校班级等场所,出现2例及以上发热或呼吸道症状的病例。\n既往史:\n有糖尿病10年,有高血压2年,有传染性疾病1年。\n体格检查:\nT:36.5℃,P:80bpm,R:20次/分,BP:120/80mmHg;\n辅助检查:\n2020年6月10日,普放:血细胞比容36.50%(偏低)40~50;单核细胞绝对值0.75*10^9/L(偏高)参考值:0.1~0.6;\n门诊诊断:\n1.高血压\n处置治疗:\n1.超声引导下甲状腺细针穿刺术;\n2.乙型肝炎表面抗体测定;\n3.膜式病变细胞采集术、后颈皮下肤层;\n电子签名:【】` + const text = `人民医院门诊病历\n主诉:\n发热三天,咳嗽五天。\n现病史:\n患者于三天前无明显诱因,感冒后发现面部水肿,无皮疹,尿量减少,出现乏力,在外治疗无好转,现来我院就诊。\n既往史:\n有糖尿病10年,有高血压2年,有传染性疾病1年。没有报告其他既往疾病。\n流行病史:\n否认14天内接触过新冠肺炎确诊患者、疑似患者、无症状感染者及其密切接触者;否认14天内去过以下场所:水产、肉类批发市场,农贸市场,集市,大型超市,夜市;否认14天内与以下场所工作人员密切接触:水产、肉类批发市场,农贸市场,集市,大型超市;否认14天内周围(如家庭、办公室)有2例以上聚集性发病;否认14天内接触过有发热或呼吸道症状的人员;否认14天内自身有发热或呼吸道症状;否认14天内接触过纳入隔离观察的人员及其他可能与新冠肺炎关联的情形;陪同家属{有无选择代码}有以上情况。\n体格检查:\nT:36.5℃,P:80bpm,R:20次/分,BP:120/80mmHg;\n辅助检查:\n2020年6月10日,普放:血细胞比容36.50%(偏低)40~50;单核细胞绝对值0.75*10^9/L(偏高)参考值:0.1~0.6;\n门诊诊断:\n1.高血压\n2.糖尿病\n3.病毒性感冒\n4.过敏性鼻炎\n5.过敏性鼻息肉\n处置治疗:\n1.超声引导下甲状腺细针穿刺术;\n2.乙型肝炎表面抗体测定;\n3.膜式病变细胞采集术、后颈皮下肤层;\n电子签名:【】` // 模拟行居中 const centerText = ['人民医院门诊病历'] const centerIndex: number[] = centerText.map(c => { @@ -13,7 +11,7 @@ window.onload = function () { return ~i ? Array(c.length).fill(i).map((_, j) => i + j) : [] }).flat() // 模拟加粗字 - const boldText = ['主诉:', '现病史:', '既往史:', '体格检查:', '辅助检查:', '门诊诊断:', '处置治疗:', '电子签名:'] + const boldText = ['主诉:', '现病史:', '既往史:', '流行病史:', '体格检查:', '辅助检查:', '门诊诊断:', '处置治疗:', '电子签名:'] const boldIndex: number[] = boldText.map(b => { const i = text.indexOf(b) return ~i ? Array(b.length).fill(i).map((_, j) => i + j) : [] @@ -64,7 +62,7 @@ window.onload = function () { size: 16 } }) - data.splice(390, 0, { + data.splice(595, 0, { value: `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFkAAAAgCAYAAAB5JtSmAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAQ0SURBVGhD7dhrUSNBFAVgvKACEVjAAhJQgAIUYAABGEAABvgfAdn6UnWou01PppOZhIXNj1P9vo9zH5PK1Waz2V5wWlxIPgMuJJ8Bi0h+fn7eXl9fb29ubrYPDw/dO/8DHh8fu/vB4kym4Orqaofb29vund8OSSbhemewSrugBMnG3vlvw9vb265yn56edmtz/t/f33+5C8MkixQSZSsl9UzLOHUmcwTYAN/Rpl5eXnY+pnIB0Xd3d7s5m3rvDsrkCGszNiQ7r/tr4v39fSc/uipOqRcqufTHBiO78GGdzG5xcLtIFmVde7L9NsvXRo9s84+Pj+79pUAwn5GcD1wIz5r+fYGeJdnjGiF9hwL7iWAcfX19/evtKVHJXrtN8Rf4A3TVczqhrut5i1mSZQgnIriSWtdzP2N+EvIhi3/GWqHWtWXuy2IYbheiKarJZIZknkxyrryc2Utrgal+9S8iScUXIx/3kcxfe/jotcuDezLFlIbARDrzHpytXdKnQr4xyc74Vu9YV5Ih2Q/tT7mDSEYw5ZU4wu3nJx64k/1z9umlUG0hah/JSbC6Jzi5exDJWoTHERoBxu8uf/pT1j3HDkUIJitjbRfRA/iwVzlgy1RCfSF5ili9xj7BUWKs9wJZ3MpditYu+lsc+/PRx53cVF9Pdg/syE9Hb6cS75PkmhUEUFofmTvLGEXKimHueJP9Y3swWQwGLUiA9xEbHKuvgs4pPe1+1myTAKlw81buJ8kigjAXKauXPLQPhEYgJSEYsgdTUR0BmTVgc6C359wcvKGnBrGO8dO5VlD1ZZ519nrBHvrwKVMCas9hgL0YUI2wV98fC4FqCWizzXyqF44A0ZKLHkilgvPs1zbiTuZIdZ414KvqGCKZYx4zple+MSrrJVncAyL02/TOqncJwVMglx5zI4QDZ5WPvBGEcNP+7TlEcqJIAQFGsIdQjmZt7MlYA5yiI3pOQTCQXUm2TuVmXgmewxDJQDgl6deJJoU5y7p9uwZagmu1mCvbNoOOBfkhOf6lRZjzPb8qRjBMMiUhM9GNMZQq5/oRXBP7Mlj/i12A7EMIaJGqDcl8I79+/N1xTvdINQ2TDAQSvI9Md479vdqCHKSFQKAfEmgBqCTDkjaSgOZXQkg2jy1ti0xApnBQJo/0obQRipeQXbN3CmxKGQch5xgki4Efghl/kFqzPD//2DnXIodIRpaoETaXxcmwGNO7N4I2Oyuc6b+xK/tL9IH3kY/E+r1JdST4yM+7VUiuJbuPZHBeHZcNvXtziMMV9mRuvUOX8Vg9IFjRx9dUYM3s2oJyNx9ahFfSWwyRHKHG3nmL2q/mojyFVAWnEdi2Hg7OBXwUCCKr1QEtoe0+/9jI3xqIiuF2QRD0zqcwpfQnge9TVSI4tWrNe79shj98F0xDC0N4bTUVF5LPgAvJJ8dm+wcP2iJuZNdC5QAAAABJRU5ErkJggg==`, width: 89, height: 32, @@ -72,7 +70,8 @@ window.onload = function () { type: ElementType.IMAGE }) // 初始化编辑器 - const instance = new Editor(canvas, data, { + const container = document.querySelector('.editor')! + const instance = new Editor(container, data, { margins: [100, 120, 100, 120] }) console.log('编辑器实例: ', instance) diff --git a/src/style.css b/src/style.css index 6f5910f..ed24899 100644 --- a/src/style.css +++ b/src/style.css @@ -249,6 +249,8 @@ ul { } .menu-item .menu-item__search__collapse { + width: 215px; + height: 36px; box-sizing: border-box; position: absolute; display: none; @@ -298,10 +300,16 @@ ul { } .editor { - width: 794px; - height: 1123px; margin: 80px auto; position: relative; +} + +.page-container { + width: 100%; +} + +.page-container canvas { + display: block; background-color: #ffffff; box-shadow: rgb(158 161 165 / 40%) 0px 2px 12px 0px; } \ No newline at end of file