diff --git a/src/editor/assets/css/index.css b/src/editor/assets/css/index.css index 8bf6c07..194c682 100644 --- a/src/editor/assets/css/index.css +++ b/src/editor/assets/css/index.css @@ -70,4 +70,41 @@ border-radius: 5px; border: 2px solid #ffffff; box-sizing: border-box; +} + +.resizer-selection .handle-0 { + cursor: nw-resize; +} + +.resizer-selection .handle-1 { + cursor: n-resize; +} + +.resizer-selection .handle-2 { + cursor: ne-resize; +} + +.resizer-selection .handle-3 { + cursor: e-resize; +} + +.resizer-selection .handle-4 { + cursor: se-resize; +} + +.resizer-selection .handle-5 { + cursor: s-resize; +} + +.resizer-selection .handle-6 { + cursor: sw-resize; +} + +.resizer-selection .handle-7 { + cursor: w-resize; +} + +.resizer-image { + position: absolute; + opacity: 0.5; } \ No newline at end of file diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts index defe96e..bb6eaac 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -67,7 +67,7 @@ export class Draw { 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.imageParticle = new ImageParticle(canvas, ctx, options, this) const canvasEvent = new CanvasEvent(canvas, this) this.cursor = new Cursor(canvas, this, canvasEvent) diff --git a/src/editor/core/draw/particle/ImageParticle.ts b/src/editor/core/draw/particle/ImageParticle.ts index d93fad3..11e349d 100644 --- a/src/editor/core/draw/particle/ImageParticle.ts +++ b/src/editor/core/draw/particle/ImageParticle.ts @@ -1,27 +1,49 @@ -import { IImageParticleCreateResult } from "../../../interface/Draw"; -import { IEditorOption } from "../../../interface/Editor"; -import { IElement, IElementPosition } from "../../../interface/Element"; +import { IImageParticleCreateResult } from "../../../interface/Draw" +import { IEditorOption } from "../../../interface/Editor" +import { IElement, IElementPosition } from "../../../interface/Element" +import { Draw } from "../Draw" export class ImageParticle { private canvas: HTMLCanvasElement private ctx: CanvasRenderingContext2D + private draw: Draw private options: Required + private curElement: IElement | null + private curPosition: IElementPosition | null private imageCache: Map private resizerSelection: HTMLDivElement private resizerHandleList: HTMLDivElement[] + private resizerImageContainer: HTMLDivElement + private resizerImage: HTMLImageElement + private width: number + private height: number + private mousedownX: number + private mousedownY: number + private curHandleIndex: number - constructor(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, options: Required) { + constructor(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, options: Required, draw: Draw) { this.canvas = canvas this.ctx = ctx + this.draw = draw this.options = options + this.curElement = null + this.curPosition = null this.imageCache = new Map() - const { resizerSelection, resizerHandleList } = this.createResizerDom() + const { resizerSelection, resizerHandleList, resizerImageContainer, resizerImage } = this.createResizerDom() this.resizerSelection = resizerSelection this.resizerHandleList = resizerHandleList + this.resizerImageContainer = resizerImageContainer + this.resizerImage = resizerImage + this.width = 0 + this.height = 0 + this.mousedownX = 0 + this.mousedownY = 0 + this.curHandleIndex = 0 // 默认右下角 } private createResizerDom(): IImageParticleCreateResult { + // 拖拽边框 const resizerSelection = document.createElement('div') resizerSelection.classList.add('resizer-selection') resizerSelection.style.display = 'none' @@ -31,11 +53,101 @@ export class ImageParticle { const handleDom = document.createElement('div') handleDom.style.background = this.options.resizerColor handleDom.classList.add(`handle-${i}`) + handleDom.setAttribute('data-index', String(i)) + handleDom.onmousedown = this.handleMousedown.bind(this) resizerSelection.append(handleDom) resizerHandleList.push(handleDom) } this.canvas.parentNode!.append(resizerSelection) - return { resizerSelection, resizerHandleList } + // 拖拽镜像 + 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) + return { resizerSelection, resizerHandleList, resizerImageContainer, resizerImage } + } + + private handleMousedown(evt: MouseEvent) { + if (!this.curPosition || !this.curElement) return + this.mousedownX = evt.x + this.mousedownY = evt.y + const target = evt.target as HTMLDivElement + this.curHandleIndex = Number(target.dataset.index) + // 改变光标 + const cursor = window.getComputedStyle(target).cursor + document.body.style.cursor = cursor + this.canvas.style.cursor = cursor + // 拖拽图片镜像 + this.resizerImage.src = this.curElement?.value! + this.resizerImageContainer.style.display = 'block' + const { coordinate: { leftTop: [left, top] } } = this.curPosition + this.resizerImageContainer.style.left = `${left}px` + this.resizerImageContainer.style.top = `${top}px` + this.resizerImage.style.width = `${this.curElement.width}px` + this.resizerImage.style.height = `${this.curElement.height}px` + // 追加全局事件 + const mousemoveFn = this.mousemove.bind(this) + document.addEventListener('mousemove', mousemoveFn) + document.addEventListener('mouseup', () => { + // 改变尺寸 + if (this.curElement && this.curPosition) { + this.curElement.width = this.width + this.curElement.height = this.height + this.draw.render({ isSetCursor: false }) + this.drawResizer(this.curElement, this.curPosition) + } + // 还原副作用 + this.resizerImageContainer.style.display = 'none' + document.removeEventListener('mousemove', mousemoveFn) + document.body.style.cursor = '' + this.canvas.style.cursor = 'text' + }, { + once: true + }) + evt.preventDefault() + } + + private mousemove(evt: MouseEvent) { + if (!this.curElement) return + let dx = 0 + let dy = 0 + switch (this.curHandleIndex) { + case 0: + dx = this.mousedownX - evt.x + dy = this.mousedownY - evt.y + break + case 1: + dy = this.mousedownY - evt.y + break + case 2: + dx = evt.x - this.mousedownX + dy = this.mousedownY - evt.y + break + case 3: + dx = evt.x - this.mousedownX + break + case 5: + dy = evt.y - this.mousedownY + break + case 6: + dx = this.mousedownX - evt.x + dy = evt.y - this.mousedownY + break + case 7: + dx = this.mousedownX - evt.x + break + default: + dx = evt.x - this.mousedownX + dy = evt.y - this.mousedownY + break + } + this.width = this.curElement.width! + dx + this.height = this.curElement.height! + dy + this.resizerImage.style.width = `${this.width}px` + this.resizerImage.style.height = `${this.height}px` + evt.preventDefault() } public getImageCache(): Map { @@ -68,6 +180,10 @@ export class ImageParticle { this.resizerHandleList[i].style.top = `${top}px` } this.resizerSelection.style.display = 'block' + this.curElement = element + this.curPosition = position + this.width = this.curElement.width! + this.height = this.curElement.height! } public clearResizer() { diff --git a/src/editor/interface/Draw.ts b/src/editor/interface/Draw.ts index 80d8263..0dfd1cc 100644 --- a/src/editor/interface/Draw.ts +++ b/src/editor/interface/Draw.ts @@ -13,4 +13,6 @@ export interface IDrawImagePayload { export interface IImageParticleCreateResult { resizerSelection: HTMLDivElement; resizerHandleList: HTMLDivElement[]; + resizerImageContainer: HTMLDivElement; + resizerImage: HTMLImageElement; } \ No newline at end of file