feat:增加分页功能

pr675
黄云飞 4 years ago
parent 493d629757
commit 7dd3c3f3c7

@ -114,9 +114,7 @@
</div>
</div>
</div>
<div class="editor" editor-component="main">
<canvas style="width: 794px;height: 1123px;"></canvas>
</div>
<div class="editor" editor-component="main"></div>
</div>
<script type="module" src="/src/main.ts"></script>
</body>

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

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

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

@ -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<IEditorOption>
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<IEditorOption>,
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<IEditorOption> {
@ -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 })

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

@ -1,43 +1,42 @@
import { IEditorOption } from "../../../interface/Editor"
import { Draw } from "../Draw"
export class Margin {
private ctx: CanvasRenderingContext2D
private options: Required<IEditorOption>
constructor(ctx: CanvasRenderingContext2D, options: Required<IEditorOption>) {
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()
}
}

@ -4,36 +4,36 @@ import { Draw } from "../Draw"
export class Search {
private ctx: CanvasRenderingContext2D
private options: Required<IEditorOption>
private draw: Draw
private options: Required<IEditorOption>
private position: Position
constructor(ctx: CanvasRenderingContext2D, options: Required<IEditorOption>, 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()
}
}

@ -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<IEditorOption>
private curElement: IElement | null
@ -22,11 +22,11 @@ export class ImageParticle {
private mousedownY: number
private curHandleIndex: number
constructor(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, options: Required<IEditorOption>, 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)
}
}

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

@ -1,22 +1,21 @@
import { IEditorOption } from "../../../interface/Editor"
import { Draw } from "../Draw"
export class Highlight {
private ctx: CanvasRenderingContext2D
private options: Required<IEditorOption>
constructor(ctx: CanvasRenderingContext2D, options: Required<IEditorOption>) {
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()
}
}

@ -1,24 +1,23 @@
import { IEditorOption } from "../../../interface/Editor"
import { Draw } from "../Draw"
export class Strikeout {
private ctx: CanvasRenderingContext2D
private options: Required<IEditorOption>
constructor(ctx: CanvasRenderingContext2D, options: Required<IEditorOption>) {
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()
}
}

@ -1,24 +1,23 @@
import { IEditorOption } from "../../../interface/Editor"
import { Draw } from "../Draw"
export class Underline {
private ctx: CanvasRenderingContext2D
private options: Required<IEditorOption>
constructor(ctx: CanvasRenderingContext2D, options: Required<IEditorOption>) {
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()
}
}

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

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

@ -14,13 +14,13 @@ export class Position {
private draw: Draw
private options: Required<IEditorOption>
constructor(options: Required<IEditorOption>, 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 }
}

@ -7,17 +7,15 @@ import { Listener } from "../listener/Listener"
export class RangeManager {
private ctx: CanvasRenderingContext2D
private draw: Draw
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
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()
}
}

@ -0,0 +1 @@
export const EDITOR_ELEMENT_STYLE = ['bold', 'color', 'highlight', 'font', 'size', 'italic', 'underline', 'strikeout']

@ -1,4 +1,5 @@
export enum KeyMap {
Delete = 'Delete',
Backspace = 'Backspace',
Enter = "Enter",
Left = "ArrowLeft",

@ -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<IEditorOption> = {
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))
}

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

@ -32,6 +32,7 @@ export interface IElementBasic {
export type IElement = IElementBasic & IElementStyle
export interface IElementPosition {
pageNo: number;
index: number;
value: string,
rowNo: number;

@ -15,4 +15,4 @@ export interface IRangeStype {
rowMargin: number
}
export type IRangeStyleChange = (payload: IRangeStype) => void;
export type IRangeStyleChange = (payload: IRangeStype) => void

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

@ -3,9 +3,7 @@ import Editor, { ElementType, IElement, RowFlex } from './editor'
window.onload = function () {
const canvas = document.querySelector<HTMLCanvasElement>('canvas')
if (!canvas) return
const text = `人民医院门诊病历\n主诉\n发热三天咳嗽五天。\n现病史\n发病前14天内有病历报告社区的旅行时或居住史发病前14天内与新型冠状病毒感染的患者或无症状感染者有接触史发病前14天内解除过来自病历报告社区的发热或有呼吸道症状的患者聚集性发病2周内在小范围如家庭、办公室、学校班级等场所出现2例及以上发热或呼吸道症状的病例。\n既往史\n有糖尿病10年有高血压2年有传染性疾病1年。\n体格检查\nT36.5℃P80bpmR20次/分BP120/80mmHg\n辅助检查\n2020年6月10日普放血细胞比容36.50%偏低4050单核细胞绝对值0.75*10^9/L偏高参考值0.10.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体格检查\nT36.5℃P80bpmR20次/分BP120/80mmHg\n辅助检查\n2020年6月10日普放血细胞比容36.50%偏低4050单核细胞绝对值0.75*10^9/L偏高参考值0.10.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<HTMLDivElement>('.editor')!
const instance = new Editor(container, data, {
margins: [100, 120, 100, 120]
})
console.log('编辑器实例: ', instance)

@ -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;
}
Loading…
Cancel
Save