feat:增加图片尺寸拖拽组件

pr675
黄云飞 4 years ago
parent 32a8b14eac
commit 13e5c83fdd

@ -56,3 +56,18 @@
opacity: 1 opacity: 1
} }
} }
.resizer-selection {
position: absolute;
border: 1px solid;
}
.resizer-selection>div {
position: absolute;
width: 10px;
height: 10px;
box-shadow: 0 1px 4px 0 rgb(0 0 0 / 30%);
border-radius: 5px;
border: 2px solid #ffffff;
box-sizing: border-box;
}

@ -1,24 +1,18 @@
import { CURSOR_AGENT_HEIGHT } from "../../dataset/constant/Cursor" import { CURSOR_AGENT_HEIGHT } from "../../dataset/constant/Cursor"
import { Draw } from "../draw/Draw" import { Draw } from "../draw/Draw"
import { CanvasEvent } from "../event/CanvasEvent" import { CanvasEvent } from "../event/CanvasEvent"
import { Position } from "../position/Position"
import { RangeManager } from "../range/RangeManager"
import { CursorAgent } from "./CursorAgent" import { CursorAgent } from "./CursorAgent"
export class Cursor { export class Cursor {
private canvas: HTMLCanvasElement private canvas: HTMLCanvasElement
private draw: Draw private draw: Draw
private range: RangeManager
private position: Position
private cursorDom: HTMLDivElement private cursorDom: HTMLDivElement
private cursorAgent: CursorAgent private cursorAgent: CursorAgent
constructor(canvas: HTMLCanvasElement, draw: Draw, canvasEvent: CanvasEvent) { constructor(canvas: HTMLCanvasElement, draw: Draw, canvasEvent: CanvasEvent) {
this.canvas = canvas this.canvas = canvas
this.draw = draw this.draw = draw
this.range = this.draw.getRange()
this.position = this.draw.getPosition()
this.cursorDom = document.createElement('div') this.cursorDom = document.createElement('div')
this.cursorDom.classList.add('cursor') this.cursorDom.classList.add('cursor')
@ -34,16 +28,6 @@ export class Cursor {
return this.cursorAgent.getAgentCursorDom() return this.cursorAgent.getAgentCursorDom()
} }
public setCursorPosition(evt: MouseEvent) {
const positionIndex = this.position.getPositionByXY(evt.offsetX, evt.offsetY)
if (~positionIndex) {
this.range.setRange(positionIndex, positionIndex)
setTimeout(() => {
this.draw.render({ curIndex: positionIndex, isSubmitHistory: false })
})
}
}
public drawCursor() { public drawCursor() {
const cursorPosition = this.draw.getPosition().getCursorPosition() const cursorPosition = this.draw.getPosition().getCursorPosition()
if (!cursorPosition) return if (!cursorPosition) return

@ -67,7 +67,7 @@ export class Draw {
this.underline = new Underline(ctx, options) this.underline = new Underline(ctx, options)
this.strikeout = new Strikeout(ctx, options) this.strikeout = new Strikeout(ctx, options)
this.highlight = new Highlight(ctx, options) this.highlight = new Highlight(ctx, options)
this.imageParticle = new ImageParticle(ctx) this.imageParticle = new ImageParticle(canvas, ctx, options)
const canvasEvent = new CanvasEvent(canvas, this) const canvasEvent = new CanvasEvent(canvas, this)
this.cursor = new Cursor(canvas, this, canvasEvent) this.cursor = new Cursor(canvas, this, canvasEvent)
@ -110,6 +110,10 @@ export class Draw {
return this.cursor return this.cursor
} }
public getImageParticle(): ImageParticle {
return this.imageParticle
}
public getRowCount(): number { public getRowCount(): number {
return this.rowCount return this.rowCount
} }
@ -153,7 +157,7 @@ export class Draw {
public render(payload?: IDrawOption) { public render(payload?: IDrawOption) {
let { curIndex, isSubmitHistory = true, isSetCursor = true } = payload || {} let { curIndex, isSubmitHistory = true, isSetCursor = true } = payload || {}
// 清除光标 // 清除光标等副作用
this.cursor.recoveryCursor() this.cursor.recoveryCursor()
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height) this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
this.position.setPositionList([]) this.position.setPositionList([])

@ -1,20 +1,80 @@
import { IElement } from "../../../interface/Element"; import { IImageParticleCreateResult } from "../../../interface/Draw";
import { IEditorOption } from "../../../interface/Editor";
import { IElement, IElementPosition } from "../../../interface/Element";
export class ImageParticle { export class ImageParticle {
private canvas: HTMLCanvasElement
private ctx: CanvasRenderingContext2D private ctx: CanvasRenderingContext2D
private imageCache: Map<string, HTMLImageElement>; private options: Required<IEditorOption>
private imageCache: Map<string, HTMLImageElement>
private resizerSelection: HTMLDivElement
private resizerHandleList: HTMLDivElement[]
constructor(ctx: CanvasRenderingContext2D) { constructor(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, options: Required<IEditorOption>) {
this.canvas = canvas
this.ctx = ctx this.ctx = ctx
this.options = options
this.imageCache = new Map() this.imageCache = new Map()
const { resizerSelection, resizerHandleList } = this.createResizerDom()
this.resizerSelection = resizerSelection
this.resizerHandleList = resizerHandleList
}
private createResizerDom(): IImageParticleCreateResult {
const resizerSelection = document.createElement('div')
resizerSelection.classList.add('resizer-selection')
resizerSelection.style.display = 'none'
resizerSelection.style.borderColor = this.options.resizerColor
const resizerHandleList: HTMLDivElement[] = []
for (let i = 0; i < 8; i++) {
const handleDom = document.createElement('div')
handleDom.style.background = this.options.resizerColor
handleDom.classList.add(`handle-${i}`)
resizerSelection.append(handleDom)
resizerHandleList.push(handleDom)
}
this.canvas.parentNode!.append(resizerSelection)
return { resizerSelection, resizerHandleList }
} }
public getImageCache(): Map<string, HTMLImageElement> { public getImageCache(): Map<string, HTMLImageElement> {
return this.imageCache return this.imageCache
} }
render(element: IElement, x: number, y: number) { public drawResizer(element: IElement, position: IElementPosition) {
const { coordinate: { leftTop: [left, top] } } = position
const width = element.width!
const height = element.height!
const handleSize = this.options.resizerSize
// 边框
this.resizerSelection.style.left = `${left}px`
this.resizerSelection.style.top = `${top}px`
this.resizerSelection.style.width = `${element.width}px`
this.resizerSelection.style.height = `${element.height}px`
// handle
for (let i = 0; i < 8; i++) {
const left = i === 0 || i === 6 || i === 7
? -handleSize
: i === 1 || i === 5
? width / 2
: width - handleSize
const top = i === 0 || i === 1 || i === 2
? -handleSize
: i === 3 || i === 7
? height / 2 - handleSize
: height - handleSize
this.resizerHandleList[i].style.left = `${left}px`
this.resizerHandleList[i].style.top = `${top}px`
}
this.resizerSelection.style.display = 'block'
}
public clearResizer() {
this.resizerSelection.style.display = 'none'
}
public render(element: IElement, x: number, y: number) {
const width = element.width! const width = element.width!
const height = element.height! const height = element.height!
if (this.imageCache.has(element.id!)) { if (this.imageCache.has(element.id!)) {

@ -5,6 +5,7 @@ import { IElement } from "../../interface/Element"
import { writeTextByElementList } from "../../utils/clipboard" import { writeTextByElementList } from "../../utils/clipboard"
import { Cursor } from "../cursor/Cursor" import { Cursor } from "../cursor/Cursor"
import { Draw } from "../draw/Draw" import { Draw } from "../draw/Draw"
import { ImageParticle } from "../draw/particle/ImageParticle"
import { HistoryManager } from "../history/HistoryManager" import { HistoryManager } from "../history/HistoryManager"
import { Position } from "../position/Position" import { Position } from "../position/Position"
import { RangeManager } from "../range/RangeManager" import { RangeManager } from "../range/RangeManager"
@ -21,6 +22,7 @@ export class CanvasEvent {
private range: RangeManager private range: RangeManager
private cursor: Cursor | null private cursor: Cursor | null
private historyManager: HistoryManager private historyManager: HistoryManager
private imageParticle: ImageParticle
constructor(canvas: HTMLCanvasElement, draw: Draw) { constructor(canvas: HTMLCanvasElement, draw: Draw) {
this.isAllowDrag = false this.isAllowDrag = false
@ -33,12 +35,12 @@ export class CanvasEvent {
this.position = this.draw.getPosition() this.position = this.draw.getPosition()
this.range = this.draw.getRange() this.range = this.draw.getRange()
this.historyManager = this.draw.getHistoryManager() this.historyManager = this.draw.getHistoryManager()
this.imageParticle = this.draw.getImageParticle()
} }
public register() { public register() {
// 延迟加载 // 延迟加载
this.cursor = this.draw.getCursor() this.cursor = this.draw.getCursor()
this.canvas.addEventListener('mousedown', this.cursor.setCursorPosition.bind(this))
this.canvas.addEventListener('mousedown', this.mousedown.bind(this)) this.canvas.addEventListener('mousedown', this.mousedown.bind(this))
this.canvas.addEventListener('mouseleave', this.mouseleave.bind(this)) this.canvas.addEventListener('mouseleave', this.mouseleave.bind(this))
this.canvas.addEventListener('mousemove', this.mousemove.bind(this)) this.canvas.addEventListener('mousemove', this.mousemove.bind(this))
@ -68,7 +70,7 @@ export class CanvasEvent {
public mousemove(evt: MouseEvent) { public mousemove(evt: MouseEvent) {
if (!this.isAllowDrag) return if (!this.isAllowDrag) return
// 结束位置 // 结束位置
const endIndex = this.draw.getPosition().getPositionByXY(evt.offsetX, evt.offsetY) const { index: endIndex } = this.draw.getPosition().getPositionByXY(evt.offsetX, evt.offsetY)
let end = ~endIndex ? endIndex : 0 let end = ~endIndex ? endIndex : 0
// 开始位置 // 开始位置
let start = this.mouseDownStartIndex let start = this.mouseDownStartIndex
@ -86,7 +88,22 @@ export class CanvasEvent {
public mousedown(evt: MouseEvent) { public mousedown(evt: MouseEvent) {
this.isAllowDrag = true this.isAllowDrag = true
this.mouseDownStartIndex = this.draw.getPosition().getPositionByXY(evt.offsetX, evt.offsetY) || 0 const { index, isDirectHit, isImage } = this.draw.getPosition().getPositionByXY(evt.offsetX, evt.offsetY)
// 记录选区开始位置
this.mouseDownStartIndex = index
// 绘制
const isDirectHitImage = isDirectHit && isImage
if (~index) {
this.range.setRange(index, index)
this.draw.render({ curIndex: index, isSubmitHistory: false, isSetCursor: !isDirectHitImage })
}
// 图片尺寸拖拽组件
this.imageParticle.clearResizer()
if (isDirectHitImage) {
const elementList = this.draw.getElementList()
const positionList = this.position.getPositionList()
this.imageParticle.drawResizer(elementList[index], positionList[index])
}
} }
public mouseleave(evt: MouseEvent) { public mouseleave(evt: MouseEvent) {

@ -2,6 +2,7 @@ import { EDITOR_COMPONENT } from "../../dataset/constant/Editor"
import { findParent } from "../../utils" import { findParent } from "../../utils"
import { Cursor } from "../cursor/Cursor" import { Cursor } from "../cursor/Cursor"
import { Draw } from "../draw/Draw" import { Draw } from "../draw/Draw"
import { ImageParticle } from "../draw/particle/ImageParticle"
import { RangeManager } from "../range/RangeManager" import { RangeManager } from "../range/RangeManager"
import { CanvasEvent } from "./CanvasEvent" import { CanvasEvent } from "./CanvasEvent"
@ -12,6 +13,7 @@ export class GlobalEvent {
private cursor: Cursor | null private cursor: Cursor | null
private canvasEvent: CanvasEvent private canvasEvent: CanvasEvent
private range: RangeManager private range: RangeManager
private imageParticle: ImageParticle
constructor(canvas: HTMLCanvasElement, draw: Draw, canvasEvent: CanvasEvent) { constructor(canvas: HTMLCanvasElement, draw: Draw, canvasEvent: CanvasEvent) {
this.canvas = canvas this.canvas = canvas
@ -19,6 +21,7 @@ export class GlobalEvent {
this.canvasEvent = canvasEvent this.canvasEvent = canvasEvent
this.cursor = null this.cursor = null
this.range = draw.getRange() this.range = draw.getRange()
this.imageParticle = draw.getImageParticle()
} }
register() { register() {
@ -56,6 +59,7 @@ export class GlobalEvent {
this.cursor.recoveryCursor() this.cursor.recoveryCursor()
this.range.recoveryRangeStyle() this.range.recoveryRangeStyle()
this.range.setRange(0, 0) this.range.setRange(0, 0)
this.imageParticle.clearResizer()
} }
setDragState() { setDragState() {

@ -1,5 +1,7 @@
import { ElementType } from "../.."
import { ZERO } from "../../dataset/constant/Common" import { ZERO } from "../../dataset/constant/Common"
import { IElement, IElementPosition } from "../../interface/Element" import { IElement, IElementPosition } from "../../interface/Element"
import { ICurrentPosition } from "../../interface/Position"
import { Draw } from "../draw/Draw" import { Draw } from "../draw/Draw"
export class Position { export class Position {
@ -34,27 +36,29 @@ export class Position {
return this.cursorPosition return this.cursorPosition
} }
public getPositionByXY(x: number, y: number): number { public getPositionByXY(x: number, y: number): ICurrentPosition {
this.elementList = this.draw.getElementList() this.elementList = this.draw.getElementList()
let isTextArea = false
for (let j = 0; j < this.positionList.length; j++) { for (let j = 0; j < this.positionList.length; j++) {
const { index, coordinate: { leftTop, rightTop, leftBottom } } = this.positionList[j]; const { index, coordinate: { leftTop, rightTop, leftBottom } } = this.positionList[j];
// 命中元素 // 命中元素
if (leftTop[0] <= x && rightTop[0] >= x && leftTop[1] <= y && leftBottom[1] >= y) { if (leftTop[0] <= x && rightTop[0] >= x && leftTop[1] <= y && leftBottom[1] >= y) {
let curPostionIndex = j let curPostionIndex = j
// 判断是否元素中间前后 const element = this.elementList[j]
// 图片区域均为命中
if (element.type === ElementType.IMAGE) {
return { index: curPostionIndex, isDirectHit: true, isImage: true }
}
// 判断是否在文字中间前后
if (this.elementList[index].value !== ZERO) { if (this.elementList[index].value !== ZERO) {
const valueWidth = rightTop[0] - leftTop[0] const valueWidth = rightTop[0] - leftTop[0]
if (x < leftTop[0] + valueWidth / 2) { if (x < leftTop[0] + valueWidth / 2) {
curPostionIndex = j - 1 curPostionIndex = j - 1
} }
} }
isTextArea = true return { index: curPostionIndex }
return curPostionIndex
} }
} }
// 非命中区域 // 非命中区域
if (!isTextArea) {
let isLastArea = false let isLastArea = false
let curPostionIndex = -1 let curPostionIndex = -1
// 判断所属行是否存在元素 // 判断所属行是否存在元素
@ -68,11 +72,9 @@ export class Position {
} }
} }
if (!isLastArea) { if (!isLastArea) {
return this.positionList.length - 1 return { index: this.positionList.length - 1 }
}
return curPostionIndex
} }
return -1 return { index: curPostionIndex }
} }
} }

@ -29,6 +29,8 @@ export default class Editor {
searchMatchAlpha: 0.6, searchMatchAlpha: 0.6,
searchMatchColor: '#FFFF00', searchMatchColor: '#FFFF00',
highlightAlpha: 0.6, highlightAlpha: 0.6,
resizerColor: '#4182D9',
resizerSize: 5,
marginIndicatorSize: 35, marginIndicatorSize: 35,
marginIndicatorColor: '#BABABA', marginIndicatorColor: '#BABABA',
margins: [100, 120, 100, 120], margins: [100, 120, 100, 120],

@ -9,3 +9,8 @@ export interface IDrawImagePayload {
height: number; height: number;
value: string; value: string;
} }
export interface IImageParticleCreateResult {
resizerSelection: HTMLDivElement;
resizerHandleList: HTMLDivElement[];
}

@ -11,6 +11,8 @@ export interface IEditorOption {
searchMatchColor?: string; searchMatchColor?: string;
searchMatchAlpha?: number; searchMatchAlpha?: number;
highlightAlpha?: number; highlightAlpha?: number;
resizerColor?: string;
resizerSize?: number;
marginIndicatorSize?: number; marginIndicatorSize?: number;
marginIndicatorColor?: string, marginIndicatorColor?: string,
margins?: [top: number, right: number, bootom: number, left: number] margins?: [top: number, right: number, bootom: number, left: number]

@ -0,0 +1,5 @@
export interface ICurrentPosition {
index: number;
isImage?: boolean;
isDirectHit?: boolean;
}
Loading…
Cancel
Save