From 79f380aef23dfbd57d8ce34897480eaf45bbb120 Mon Sep 17 00:00:00 2001 From: chenshenhai Date: Sun, 10 Mar 2024 17:10:41 +0800 Subject: [PATCH] refactor: refactor mode of idraw --- packages/board/src/index.ts | 61 +++--- .../core/src/middleware/selector/auxiliary.ts | 18 ++ .../core/src/middleware/selector/config.ts | 2 + .../src/middleware/selector/draw-auxiliary.ts | 80 +++----- .../core/src/middleware/selector/draw-base.ts | 41 +++- .../src/middleware/selector/draw-wrapper.ts | 9 +- .../core/src/middleware/text-editor/index.ts | 13 ++ packages/idraw/src/config.ts | 24 ++- packages/idraw/src/idraw.ts | 93 +++++---- packages/idraw/src/index.ts | 6 +- packages/idraw/src/mode.ts | 95 +++++++++ packages/types/src/lib/idraw.ts | 21 +- packages/types/src/lib/view.ts | 30 ++- packages/util/src/index.ts | 7 +- packages/util/src/lib/element.ts | 17 ++ packages/util/src/lib/store.ts | 3 +- packages/util/src/lib/view-calc.ts | 187 +++++++++++++++++- packages/util/src/lib/view-visible.ts | 49 +++++ 18 files changed, 597 insertions(+), 159 deletions(-) create mode 100644 packages/core/src/middleware/selector/auxiliary.ts create mode 100644 packages/idraw/src/mode.ts create mode 100644 packages/util/src/lib/view-visible.ts diff --git a/packages/board/src/index.ts b/packages/board/src/index.ts index 1ddba58..6ba515c 100644 --- a/packages/board/src/index.ts +++ b/packages/board/src/index.ts @@ -1,5 +1,9 @@ import { Renderer } from '@idraw/renderer'; -import { throttle, calcElementsContextSize, EventEmitter } from '@idraw/util'; +import { + // throttle, + calcElementsContextSize, + EventEmitter +} from '@idraw/util'; import type { Data, BoardOptions, @@ -16,7 +20,7 @@ import { BoardWatcher } from './lib/watcher'; import { Sharer } from './lib/sharer'; import { Viewer } from './lib/viewer'; -const throttleTime = 10; // ms +// const throttleTime = 10; // ms interface BoardMiddlewareMapItem { status: 'enable' | 'disable'; @@ -91,31 +95,34 @@ export class Board { #init() { this.#watcher.on('pointStart', this.#handlePointStart.bind(this)); this.#watcher.on('pointEnd', this.#handlePointEnd.bind(this)); - this.#watcher.on( - 'pointMove', - throttle((e) => { - this.#handlePointMove(e); - }, throttleTime) - ); - this.#watcher.on( - 'hover', - throttle((e) => { - this.#handleHover(e); - }, throttleTime) - ); - - this.#watcher.on( - 'wheel', - throttle((e) => { - this.#handleWheel(e); - }, throttleTime) - ); - this.#watcher.on( - 'wheelScale', - throttle((e) => { - this.#handleWheelScale(e); - }, throttleTime) - ); + // this.#watcher.on( + // 'pointMove', + // throttle((e) => { + // this.#handlePointMove(e); + // }, throttleTime) + // ); + // this.#watcher.on( + // 'hover', + // throttle((e) => { + // this.#handleHover(e); + // }, throttleTime) + // ); + // this.#watcher.on( + // 'wheel', + // throttle((e) => { + // this.#handleWheel(e); + // }, throttleTime) + // ); + // this.#watcher.on( + // 'wheelScale', + // throttle((e) => { + // this.#handleWheelScale(e); + // }, throttleTime) + // ); + this.#watcher.on('pointMove', this.#handlePointMove.bind(this)); + this.#watcher.on('hover', this.#handleHover.bind(this)); + this.#watcher.on('wheel', this.#handleWheel.bind(this)); + this.#watcher.on('wheelScale', this.#handleWheelScale.bind(this)); this.#watcher.on('scrollX', this.#handleScrollX.bind(this)); this.#watcher.on('scrollY', this.#handleScrollY.bind(this)); this.#watcher.on('resize', this.#handleResize.bind(this)); diff --git a/packages/core/src/middleware/selector/auxiliary.ts b/packages/core/src/middleware/selector/auxiliary.ts new file mode 100644 index 0000000..1bba63d --- /dev/null +++ b/packages/core/src/middleware/selector/auxiliary.ts @@ -0,0 +1,18 @@ +import type { Element, ElementSize, ViewRectInfo, ViewScaleInfo } from '@idraw/types'; +import { calcElementViewRectInfo } from '@idraw/util'; + +export function getElementViewRectInfo( + elem: ElementSize, + opts: { + groupQueue: Element<'group'>[]; + viewScaleInfo: ViewScaleInfo; + } +): ViewRectInfo { + const { groupQueue, viewScaleInfo } = opts; + const viewRectInfo = calcElementViewRectInfo(elem, { + groupQueue, + viewScaleInfo, + range: true + }); + return viewRectInfo; +} diff --git a/packages/core/src/middleware/selector/config.ts b/packages/core/src/middleware/selector/config.ts index 03f23e2..f55de56 100644 --- a/packages/core/src/middleware/selector/config.ts +++ b/packages/core/src/middleware/selector/config.ts @@ -30,3 +30,5 @@ export const lockColor = '#5b5959b5'; export const controllerSize = 10; // export const wrapperColor = '#1890ff'; + +export const auxiliaryColor = '#f7276e'; diff --git a/packages/core/src/middleware/selector/draw-auxiliary.ts b/packages/core/src/middleware/selector/draw-auxiliary.ts index 7c8b00e..c72998d 100644 --- a/packages/core/src/middleware/selector/draw-auxiliary.ts +++ b/packages/core/src/middleware/selector/draw-auxiliary.ts @@ -1,69 +1,43 @@ -import type { ViewContext2D, ViewRectVertexes, Element } from '@idraw/types'; -import { drawLine } from './draw-base'; +import type { ViewContext2D, ViewRectVertexes, Element, ViewScaleInfo } from '@idraw/types'; +import { getElementViewRectInfo } from './auxiliary'; +import { auxiliaryColor } from './config'; +import { drawLine, drawCrossByCenter } from './draw-base'; -const auxiliaryColor = '#f7276e'; -// const auxiliaryTextColor = '#FFFFFF'; -// const fontSize = 12; -// const fontFamily = 'Consolas,Monaco,monospace'; -// const fontWeight = 600; - -// function drawLabel( -// ctx, -// opts: { -// text: string; -// start: PointSize; -// textColor: string; -// background: string; -// } -// ) { -// // TODO -// } - -export function drawSizeAuxiliaryLines( +export function drawAuxiliaryLines( ctx: ViewContext2D, opts: { vertexes: ViewRectVertexes; element: Element | null; groupQueue: Element<'group'>[]; + viewScaleInfo: ViewScaleInfo; } ) { - const { vertexes, element, groupQueue } = opts; - const point = vertexes[0]; + const { element, groupQueue, viewScaleInfo } = opts; + if (!element) { + return; + } + const viewRectInfo = getElementViewRectInfo(element, { + groupQueue, + viewScaleInfo + }); const lineOpts = { borderColor: auxiliaryColor, borderWidth: 1, lineDash: [] - // lineDash: [4, 4] }; - if (groupQueue.length > 0) { - // TODO - } else { - if (!element?.angle) { - drawLine(ctx, { x: point.x, y: 0 }, point, lineOpts); - drawLine(ctx, { x: 0, y: point.y }, point, lineOpts); + drawLine(ctx, viewRectInfo.topLeft, viewRectInfo.topRight, lineOpts); + drawLine(ctx, viewRectInfo.topRight, viewRectInfo.bottomRight, lineOpts); + drawLine(ctx, viewRectInfo.bottomRight, viewRectInfo.bottomLeft, lineOpts); + drawLine(ctx, viewRectInfo.bottomLeft, viewRectInfo.topLeft, lineOpts); - // { - // const xNum = `${element?.x}`; - // const w = xNum.length * fontSize; - // const h = fontSize; - // const x = point.x / 2 - xNum.length * fontSize; - // const y = point.y + fontSize / 2; - // ctx.$setFont({ - // fontSize, - // fontFamily, - // fontWeight - // }); - // ctx.moveTo(x, y); - // ctx.lineTo(x + w, y); - // ctx.lineTo(x + w, y + h); - // ctx.lineTo(x, y + h); - // ctx.lineTo(x, y); - // ctx.fillStyle = auxiliaryColor; - // ctx.fill(); + const crossOpts = { ...lineOpts, size: 6 }; - // ctx.fillStyle = auxiliaryTextColor; - // ctx.fillText(xNum, x, y, w); - // } - } - } + drawCrossByCenter(ctx, viewRectInfo.topLeft, crossOpts); + drawCrossByCenter(ctx, viewRectInfo.topRight, crossOpts); + drawCrossByCenter(ctx, viewRectInfo.bottomLeft, crossOpts); + drawCrossByCenter(ctx, viewRectInfo.bottomRight, crossOpts); + drawCrossByCenter(ctx, viewRectInfo.top, crossOpts); + drawCrossByCenter(ctx, viewRectInfo.right, crossOpts); + drawCrossByCenter(ctx, viewRectInfo.bottom, crossOpts); + drawCrossByCenter(ctx, viewRectInfo.left, crossOpts); } diff --git a/packages/core/src/middleware/selector/draw-base.ts b/packages/core/src/middleware/selector/draw-base.ts index 543adf6..2c112fa 100644 --- a/packages/core/src/middleware/selector/draw-base.ts +++ b/packages/core/src/middleware/selector/draw-base.ts @@ -85,16 +85,12 @@ export function drawCircleController( // ctx.fill(); } -export function drawCrossVertexes( - ctx: ViewContext2D, - vertexes: ViewRectVertexes, - opts: { borderColor: string; borderWidth: number; background: string; lineDash: number[] } -) { - const { borderColor, borderWidth, background, lineDash } = opts; +export function drawCrossVertexes(ctx: ViewContext2D, vertexes: ViewRectVertexes, opts: { borderColor: string; borderWidth: number; lineDash: number[] }) { + const { borderColor, borderWidth, lineDash } = opts; ctx.setLineDash([]); ctx.lineWidth = borderWidth; ctx.strokeStyle = borderColor; - ctx.fillStyle = background; + // ctx.fillStyle = background; ctx.setLineDash(lineDash); ctx.beginPath(); ctx.moveTo(vertexes[0].x, vertexes[0].y); @@ -107,3 +103,34 @@ export function drawCrossVertexes( ctx.closePath(); ctx.stroke(); } + +export function drawCrossByCenter(ctx: ViewContext2D, center: PointSize, opts: { size: number; borderColor: string; borderWidth: number; lineDash: number[] }) { + const { size, borderColor, borderWidth, lineDash } = opts; + const minX = center.x - size / 2; + const maxX = center.x + size / 2; + const minY = center.y - size / 2; + const maxY = center.y + size / 2; + const vertexes: ViewRectVertexes = [ + { + x: minX, + y: minY + }, + { + x: maxX, + y: minY + }, + { + x: maxX, + y: maxY + }, + { + x: minX, + y: maxY + } + ]; + drawCrossVertexes(ctx, vertexes, { + borderColor, + borderWidth, + lineDash + }); +} diff --git a/packages/core/src/middleware/selector/draw-wrapper.ts b/packages/core/src/middleware/selector/draw-wrapper.ts index 97bf0a4..185ab0f 100644 --- a/packages/core/src/middleware/selector/draw-wrapper.ts +++ b/packages/core/src/middleware/selector/draw-wrapper.ts @@ -13,7 +13,7 @@ import { rotateElementVertexes, calcViewPointSize, calcViewVertexes } from '@idr import type { AreaSize } from './types'; import { resizeControllerBorderWidth, areaBorderWidth, wrapperColor, selectWrapperBorderWidth, lockColor, controllerSize } from './config'; import { drawVertexes, drawLine, drawCircleController, drawCrossVertexes } from './draw-base'; -// import { drawSizeAuxiliaryLines } from './draw-auxiliary'; +// import { drawAuxiliaryLines } from './draw-auxiliary'; export function drawHoverVertexesWrapper( ctx: ViewContext2D, @@ -77,7 +77,7 @@ export function drawSelectedElementControllersVertexes( return; } const { hideControllers } = opts; - // const { element, groupQueue } = opts; + // const { element, groupQueue, viewScaleInfo } = opts; const { elementWrapper, topLeft, topRight, bottomLeft, bottomRight, top, rotate } = controller; const wrapperOpts = { borderColor: wrapperColor, borderWidth: selectWrapperBorderWidth, background: 'transparent', lineDash: [] }; const ctrlOpts = { ...wrapperOpts, borderWidth: resizeControllerBorderWidth, background: '#FFFFFF' }; @@ -96,7 +96,7 @@ export function drawSelectedElementControllersVertexes( drawCircleController(ctx, calcViewPointSize(rotate.center, opts), { ...ctrlOpts, size: controllerSize, borderWidth: 2 }); } - // drawSizeAuxiliaryLines(ctx, { + // drawAuxiliaryLines(ctx, { // vertexes: [ // calcViewPointSize(topLeft.center, opts), // calcViewPointSize(topRight.center, opts), @@ -104,7 +104,8 @@ export function drawSelectedElementControllersVertexes( // calcViewPointSize(bottomLeft.center, opts) // ], // element, - // groupQueue + // groupQueue, + // viewScaleInfo // }); } diff --git a/packages/core/src/middleware/text-editor/index.ts b/packages/core/src/middleware/text-editor/index.ts index d74b584..cf48aa9 100644 --- a/packages/core/src/middleware/text-editor/index.ts +++ b/packages/core/src/middleware/text-editor/index.ts @@ -220,6 +220,19 @@ export const MiddlewareTextEditor: BoardMiddleware { + e.stopPropagation(); + }); + textarea.addEventListener('keypress', (e) => { + e.stopPropagation(); + }); + textarea.addEventListener('keyup', (e) => { + e.stopPropagation(); + }); + textarea.addEventListener('wheel', (e) => { + e.stopPropagation(); + e.preventDefault(); + }); const textEditCallback = (e: TextEditEvent) => { if (e?.position && e?.element && e?.element?.type === 'text') { diff --git a/packages/idraw/src/config.ts b/packages/idraw/src/config.ts index 3f59234..fac8b6b 100644 --- a/packages/idraw/src/config.ts +++ b/packages/idraw/src/config.ts @@ -1,10 +1,20 @@ -import { IDrawSettings } from '@idraw/types'; +import type { IDrawSettings, IDrawStorage, IDrawMode } from '@idraw/types'; + +export const defaultMode: IDrawMode = 'select'; export const defaultSettings: Required = { - enableScroll: true, - enableSelect: true, - enableScale: true, - enableRuler: true, - enableTextEdit: true, - enableDrag: false + mode: defaultMode }; + +export function getDefaultStorage(): IDrawStorage { + const storage: IDrawStorage = { + mode: defaultMode, + enableRuler: false, + enableScale: false, + enableScroll: false, + enableSelect: false, + enableTextEdit: false, + enableDrag: false + }; + return storage; +} diff --git a/packages/idraw/src/idraw.ts b/packages/idraw/src/idraw.ts index a0bb877..ce91698 100644 --- a/packages/idraw/src/idraw.ts +++ b/packages/idraw/src/idraw.ts @@ -1,15 +1,18 @@ -import { Core, MiddlewareSelector, MiddlewareScroller, MiddlewareScaler, MiddlewareRuler, MiddlewareTextEditor, MiddlewareDragger } from '@idraw/core'; +import { Core } from '@idraw/core'; import type { PointSize, IDrawOptions, IDrawSettings, + IDrawFeature, + IDrawMode, Data, ViewSizeInfo, ViewScaleInfo, ElementType, Element, RecursivePartial, - ElementPosition + ElementPosition, + IDrawStorage } from '@idraw/types'; import type { IDrawEvent } from './event'; import { @@ -22,16 +25,21 @@ import { calcElementListSize, filterCompactData, calcViewCenterContent, - calcViewCenter + calcViewCenter, + Store } from '@idraw/util'; -import { defaultSettings } from './config'; +import { defaultSettings, getDefaultStorage, defaultMode } from './config'; import { exportImageFileBlobURL } from './file'; import type { ExportImageFileBaseOptions, ExportImageFileResult } from './file'; import { eventKeys } from './event'; +import { changeMode } from './mode'; export class iDraw { #core: Core; #opts: IDrawOptions; + #store: Store = new Store({ + defaultStorage: getDefaultStorage() + }); constructor(mount: HTMLDivElement, options: IDrawOptions) { const opts = { ...defaultSettings, ...options }; @@ -43,63 +51,48 @@ export class iDraw { } #init() { - const { enableRuler, enableScale, enableScroll, enableSelect, enableTextEdit, enableDrag } = this.#opts; const core = this.#core; - enableScroll === true && core.use(MiddlewareScroller); - enableSelect === true && core.use(MiddlewareSelector); - enableScale === true && core.use(MiddlewareScaler); - enableRuler === true && core.use(MiddlewareRuler); - enableTextEdit === true && core.use(MiddlewareTextEditor); - enableDrag === true && core.use(MiddlewareDragger); + const store = this.#store; + changeMode('select', core, store); + this.enable('ruler'); + } + + #setFeature(feat: IDrawFeature, status: boolean) { + if (feat === 'ruler') { + const store = this.#store; + store.set('enableRuler', !!status); + const currentMode = store.get('mode'); + this.setMode(currentMode); + } } reset(opts: IDrawSettings) { const core = this.#core; - const { enableRuler, enableScale, enableScroll, enableSelect, enableTextEdit, enableDrag } = opts; - if (enableScroll === true) { - core.use(MiddlewareScroller); - } else if (enableScroll === false) { - core.disuse(MiddlewareScroller); - } - - if (enableSelect === true) { - core.use(MiddlewareSelector); - } else if (enableSelect === false) { - core.disuse(MiddlewareSelector); - } - - if (enableScale === true) { - core.use(MiddlewareScaler); - } else if (enableScale === false) { - core.disuse(MiddlewareScaler); - } - - if (enableRuler === true) { - core.use(MiddlewareRuler); - } else if (enableRuler === false) { - core.disuse(MiddlewareRuler); - } - - if (enableTextEdit === true) { - core.use(MiddlewareTextEditor); - } else if (enableTextEdit === false) { - core.disuse(MiddlewareTextEditor); - } - - if (enableDrag === true) { - core.use(MiddlewareDragger); - } else if (enableDrag === false) { - core.disuse(MiddlewareDragger); - } - + const store = this.#store; + store.clear(); + changeMode(opts.mode || defaultMode, core, store); core.refresh(); - this.#opts = { ...this.#opts, ...opts }; } + setMode(mode: IDrawMode) { + const core = this.#core; + const store = this.#store; + changeMode(mode || defaultMode, core, store); + core.refresh(); + } + + enable(feat: IDrawFeature) { + this.#setFeature(feat, true); + } + + disable(feat: IDrawFeature) { + this.#setFeature(feat, false); + } + setData(data: Data) { const core = this.#core; core.setData(data); @@ -273,7 +266,9 @@ export class iDraw { destroy() { const core = this.#core; + const store = this.#store; core.destroy(); + store.destroy(); } getViewCenter() { diff --git a/packages/idraw/src/index.ts b/packages/idraw/src/index.ts index 1218167..59169ce 100644 --- a/packages/idraw/src/index.ts +++ b/packages/idraw/src/index.ts @@ -118,7 +118,11 @@ export { deepCloneElement, calcViewCenterContent, calcViewCenter, - modifyElement + modifyElement, + calcElementViewRectInfo, + calcElementOriginRectInfo, + calcElementViewRectInfoMap, + sortElementsViewVisiableInfoMap } from '@idraw/util'; export { iDraw } from './idraw'; export { eventKeys } from './event'; diff --git a/packages/idraw/src/mode.ts b/packages/idraw/src/mode.ts new file mode 100644 index 0000000..a9b1473 --- /dev/null +++ b/packages/idraw/src/mode.ts @@ -0,0 +1,95 @@ +import type { IDrawMode, IDrawStorage } from '@idraw/types'; +import { Store } from '@idraw/util'; +import { Core, MiddlewareSelector, MiddlewareScroller, MiddlewareScaler, MiddlewareRuler, MiddlewareTextEditor, MiddlewareDragger } from '@idraw/core'; +import { IDrawEvent } from './event'; + +function isValidMode(mode: string | IDrawMode) { + return ['select', 'drag', 'readOnly'].includes(mode); +} + +function runMiddlewares(core: Core, store: Store) { + const { enableRuler, enableScale, enableScroll, enableSelect, enableTextEdit, enableDrag } = store.getSnapshot(); + if (enableScroll === true) { + core.use(MiddlewareScroller); + } else if (enableScroll === false) { + core.disuse(MiddlewareScroller); + } + + if (enableSelect === true) { + core.use(MiddlewareSelector); + } else if (enableSelect === false) { + core.disuse(MiddlewareSelector); + } + + if (enableScale === true) { + core.use(MiddlewareScaler); + } else if (enableScale === false) { + core.disuse(MiddlewareScaler); + } + + if (enableRuler === true) { + core.use(MiddlewareRuler); + } else if (enableRuler === false) { + core.disuse(MiddlewareRuler); + } + + if (enableTextEdit === true) { + core.use(MiddlewareTextEditor); + } else if (enableTextEdit === false) { + core.disuse(MiddlewareTextEditor); + } + + if (enableDrag === true) { + core.use(MiddlewareDragger); + } else if (enableDrag === false) { + core.disuse(MiddlewareDragger); + } +} + +export function changeMode(mode: IDrawMode, core: Core, store: Store) { + let enableScale: boolean = false; + let enableScroll: boolean = false; + let enableSelect: boolean = false; + let enableTextEdit: boolean = false; + let enableDrag: boolean = false; + let enableRuler = store.get('enableRuler'); + + let innerMode: IDrawMode = 'select'; + store.set('mode', innerMode); + if (isValidMode(mode)) { + innerMode = mode; + } else { + // eslint-disable-next-line no-console + console.warn(`${mode} is invalid mode of iDraw.js`); + } + + if (innerMode === 'select') { + enableScale = true; + enableScroll = true; + enableSelect = true; + enableTextEdit = true; + enableDrag = false; + } else if (innerMode === 'drag') { + enableScale = true; + enableScroll = true; + enableSelect = false; + enableTextEdit = false; + enableDrag = true; + } else if (innerMode === 'readOnly') { + enableScale = false; + enableScroll = false; + enableSelect = false; + enableTextEdit = false; + enableDrag = false; + enableRuler = false; + } + + store.set('enableScale', enableScale); + store.set('enableScroll', enableScroll); + store.set('enableSelect', enableSelect); + store.set('enableTextEdit', enableTextEdit); + store.set('enableDrag', enableDrag); + store.set('enableRuler', enableRuler); + + runMiddlewares(core, store); +} diff --git a/packages/types/src/lib/idraw.ts b/packages/types/src/lib/idraw.ts index 62d35f4..803da15 100644 --- a/packages/types/src/lib/idraw.ts +++ b/packages/types/src/lib/idraw.ts @@ -1,12 +1,21 @@ import type { CoreOptions } from './core'; +export type IDrawMode = 'select' | 'drag' | 'readOnly'; + +export type IDrawFeature = 'ruler' | string; // TODO other feature + export interface IDrawSettings { - enableScroll?: boolean; - enableSelect?: boolean; - enableScale?: boolean; - enableRuler?: boolean; - enableTextEdit?: boolean; - enableDrag?: boolean; + mode?: IDrawMode; } export type IDrawOptions = CoreOptions & IDrawSettings; + +export interface IDrawStorage { + mode: IDrawMode; + enableRuler: boolean; + enableScale: boolean; + enableScroll: boolean; + enableSelect: boolean; + enableTextEdit: boolean; + enableDrag: boolean; +} diff --git a/packages/types/src/lib/view.ts b/packages/types/src/lib/view.ts index 65d57d4..cc2348a 100644 --- a/packages/types/src/lib/view.ts +++ b/packages/types/src/lib/view.ts @@ -1,4 +1,4 @@ -import type { Element, ElementType, ElementSize } from './element'; +import type { Element, ElementType, ElementSize, ElementPosition } from './element'; import type { Point, PointSize } from './point'; import type { Data } from './data'; import type { ViewContext2D } from './context2d'; @@ -54,3 +54,31 @@ export interface ViewBoxSize { h: number; radiusList: [number, number, number, number]; } + +export type ViewRectInfo = { + topLeft: PointSize; + topRight: PointSize; + bottomRight: PointSize; + bottomLeft: PointSize; + top: PointSize; + right: PointSize; + bottom: PointSize; + left: PointSize; + center: PointSize; +}; + +export type ViewRectInfoMap = { + originRectInfo: ViewRectInfo; + viewRectInfo: ViewRectInfo | null; + rangeRectInfo: ViewRectInfo | null; +}; + +export type ViewVisibleInfo = ViewRectInfoMap & { + isVisibleInView: boolean; + isGroup: boolean; + position: ElementPosition; +}; + +export type ViewVisibleInfoMap = { + [uuid: string]: ViewVisibleInfo; +}; diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts index 039183b..903ea2f 100644 --- a/packages/util/src/index.ts +++ b/packages/util/src/index.ts @@ -37,6 +37,7 @@ export { findElementQueueFromListByPosition, findElementsFromListByPositions, getGroupQueueFromList, + getGroupQueueByElementPosition, getElementSize, mergeElementAsset, filterElementAsset, @@ -54,8 +55,12 @@ export { isViewPointInElement, getViewPointAtElement, isElementInView, - calcViewScaleInfo + calcViewScaleInfo, + calcElementViewRectInfo, + calcElementOriginRectInfo, + calcElementViewRectInfoMap } from './lib/view-calc'; +export { sortElementsViewVisiableInfoMap } from './lib/view-visible'; export { rotatePoint, rotateVertexes, rotateByCenter } from './lib/rotate'; export { getElementVertexes, calcElementVertexesInGroup, calcElementVertexesQueueInGroup, calcElementQueueVertexesQueueInGroup } from './lib/vertex'; export { calcElementSizeController } from './lib/controller'; diff --git a/packages/util/src/lib/element.ts b/packages/util/src/lib/element.ts index 76e301a..46d0d32 100644 --- a/packages/util/src/lib/element.ts +++ b/packages/util/src/lib/element.ts @@ -293,6 +293,23 @@ export function getGroupQueueFromList(uuid: string, elements: Element[], position: ElementPosition): Element<'group'>[] | null { + const groupQueue: Element<'group'>[] = []; + let currentElements: Element[] = elements; + if (position.length > 1) { + for (let i = 0; i < position.length - 1; i++) { + const group = currentElements[i] as Element<'group'>; + if (group?.type === 'group' && Array.isArray(group?.detail?.children)) { + groupQueue.push(group); + currentElements = group.detail.children; + } else { + return null; + } + } + } + return groupQueue; +} + export function getElementSize(elem: Element): ElementSize { const { x, y, w, h, angle } = elem; const size: ElementSize = { x, y, w, h, angle }; diff --git a/packages/util/src/lib/store.ts b/packages/util/src/lib/store.ts index 87cf336..1cd24b2 100644 --- a/packages/util/src/lib/store.ts +++ b/packages/util/src/lib/store.ts @@ -18,7 +18,8 @@ export class Store = Record[]; + } +): ViewRectInfo { + const { groupQueue } = opts; + + const vertexes = calcElementVertexesInGroup(elemSize, { groupQueue }) as ViewRectVertexes; + + const top = getCenterFromTwoPoints(vertexes[0], vertexes[1]); + const right = getCenterFromTwoPoints(vertexes[1], vertexes[2]); + const bottom = getCenterFromTwoPoints(vertexes[2], vertexes[3]); + const left = getCenterFromTwoPoints(vertexes[3], vertexes[0]); + + const topLeft = vertexes[0]; + const topRight = vertexes[1]; + const bottomRight = vertexes[2]; + const bottomLeft = vertexes[3]; + + const maxX = Math.max(topLeft.x, topRight.x, bottomRight.x, bottomLeft.x); + const maxY = Math.max(topLeft.y, topRight.y, bottomRight.y, bottomLeft.y); + const minX = Math.min(topLeft.x, topRight.x, bottomRight.x, bottomLeft.x); + const minY = Math.min(topLeft.y, topRight.y, bottomRight.y, bottomLeft.y); + const center: PointSize = { + x: (maxX + minX) / 2, + y: (maxY + minY) / 2 + }; + + const rectInfo: ViewRectInfo = { + center, + topLeft, + topRight, + bottomLeft, + bottomRight, + top, + right, + left, + bottom + }; + + return rectInfo; +} + +export function calcElementViewRectInfo( + elemSize: ElementSize, + opts: { + groupQueue: Element<'group'>[]; + viewScaleInfo: ViewScaleInfo; + range?: boolean; + } +): ViewRectInfo { + const { groupQueue, viewScaleInfo, range } = opts; + + // Original RectInfo + const originRectInfo = calcElementOriginRectInfo(elemSize, { groupQueue }); + const { center, top, bottom, left, right, topLeft, topRight, bottomLeft, bottomRight } = originRectInfo; + + // View RectInfo + const viewRectInfo: ViewRectInfo = { + center: calcViewPointSize(center, { viewScaleInfo }), + topLeft: calcViewPointSize(topLeft, { viewScaleInfo }), + topRight: calcViewPointSize(topRight, { viewScaleInfo }), + bottomLeft: calcViewPointSize(bottomLeft, { viewScaleInfo }), + bottomRight: calcViewPointSize(bottomRight, { viewScaleInfo }), + top: calcViewPointSize(top, { viewScaleInfo }), + right: calcViewPointSize(right, { viewScaleInfo }), + left: calcViewPointSize(left, { viewScaleInfo }), + bottom: calcViewPointSize(bottom, { viewScaleInfo }) + }; + + if (range === true) { + // Range RectInfo + const viewMaxX = Math.max(viewRectInfo.topLeft.x, viewRectInfo.topRight.x, viewRectInfo.bottomRight.x, viewRectInfo.bottomLeft.x); + const viewMaxY = Math.max(viewRectInfo.topLeft.y, viewRectInfo.topRight.y, viewRectInfo.bottomRight.y, viewRectInfo.bottomLeft.y); + const viewMinX = Math.min(viewRectInfo.topLeft.x, viewRectInfo.topRight.x, viewRectInfo.bottomRight.x, viewRectInfo.bottomLeft.x); + const viewMinY = Math.min(viewRectInfo.topLeft.y, viewRectInfo.topRight.y, viewRectInfo.bottomRight.y, viewRectInfo.bottomLeft.y); + + const rangeCenter = { x: viewRectInfo.center.x, y: viewRectInfo.center.y }; + const rangeTopLeft = { x: viewMinX, y: viewMinY }; + const rangeTopRight = { x: viewMaxX, y: viewMinY }; + const rangeBottomRight = { x: viewMaxX, y: viewMaxY }; + const rangeBottomLeft = { x: viewMinX, y: viewMaxY }; + + const rangeTop = getCenterFromTwoPoints(rangeTopLeft, rangeTopRight); + const rangeBottom = getCenterFromTwoPoints(rangeBottomLeft, rangeBottomRight); + const rangeLeft = getCenterFromTwoPoints(rangeTopLeft, rangeBottomLeft); + const rangeRight = getCenterFromTwoPoints(rangeTopRight, rangeBottomRight); + + const rangeRectInfo: ViewRectInfo = { + center: rangeCenter, + topLeft: rangeTopLeft, + topRight: rangeTopRight, + bottomLeft: rangeBottomLeft, + bottomRight: rangeBottomRight, + top: rangeTop, + right: rangeRight, + left: rangeLeft, + bottom: rangeBottom + }; + return rangeRectInfo; + } + + return viewRectInfo; +} + +export function calcElementViewRectInfoMap( + elemSize: ElementSize, + opts: { + groupQueue: Element<'group'>[]; + viewScaleInfo: ViewScaleInfo; + } +): ViewRectInfoMap { + const { groupQueue, viewScaleInfo } = opts; + + // Original RectInfo + const originRectInfo = calcElementOriginRectInfo(elemSize, { groupQueue }); + const { center, top, bottom, left, right, topLeft, topRight, bottomLeft, bottomRight } = originRectInfo; + + // View RectInfo + const viewRectInfo: ViewRectInfo = { + center: calcViewPointSize(center, { viewScaleInfo }), + topLeft: calcViewPointSize(topLeft, { viewScaleInfo }), + topRight: calcViewPointSize(topRight, { viewScaleInfo }), + bottomLeft: calcViewPointSize(bottomLeft, { viewScaleInfo }), + bottomRight: calcViewPointSize(bottomRight, { viewScaleInfo }), + top: calcViewPointSize(top, { viewScaleInfo }), + right: calcViewPointSize(right, { viewScaleInfo }), + left: calcViewPointSize(left, { viewScaleInfo }), + bottom: calcViewPointSize(bottom, { viewScaleInfo }) + }; + + // Range RectInfo + const viewMaxX = Math.max(viewRectInfo.topLeft.x, viewRectInfo.topRight.x, viewRectInfo.bottomRight.x, viewRectInfo.bottomLeft.x); + const viewMaxY = Math.max(viewRectInfo.topLeft.y, viewRectInfo.topRight.y, viewRectInfo.bottomRight.y, viewRectInfo.bottomLeft.y); + const viewMinX = Math.min(viewRectInfo.topLeft.x, viewRectInfo.topRight.x, viewRectInfo.bottomRight.x, viewRectInfo.bottomLeft.x); + const viewMinY = Math.min(viewRectInfo.topLeft.y, viewRectInfo.topRight.y, viewRectInfo.bottomRight.y, viewRectInfo.bottomLeft.y); + + const rangeCenter = { x: viewRectInfo.center.x, y: viewRectInfo.center.y }; + const rangeTopLeft = { x: viewMinX, y: viewMinY }; + const rangeTopRight = { x: viewMaxX, y: viewMinY }; + const rangeBottomRight = { x: viewMaxX, y: viewMaxY }; + const rangeBottomLeft = { x: viewMinX, y: viewMaxY }; + + const rangeTop = getCenterFromTwoPoints(rangeTopLeft, rangeTopRight); + const rangeBottom = getCenterFromTwoPoints(rangeBottomLeft, rangeBottomRight); + const rangeLeft = getCenterFromTwoPoints(rangeTopLeft, rangeBottomLeft); + const rangeRight = getCenterFromTwoPoints(rangeTopRight, rangeBottomRight); + + const rangeRectInfo: ViewRectInfo = { + center: rangeCenter, + topLeft: rangeTopLeft, + topRight: rangeTopRight, + bottomLeft: rangeBottomLeft, + bottomRight: rangeBottomRight, + top: rangeTop, + right: rangeRight, + left: rangeLeft, + bottom: rangeBottom + }; + + return { + originRectInfo, + viewRectInfo, + rangeRectInfo + }; +} diff --git a/packages/util/src/lib/view-visible.ts b/packages/util/src/lib/view-visible.ts new file mode 100644 index 0000000..b3446c5 --- /dev/null +++ b/packages/util/src/lib/view-visible.ts @@ -0,0 +1,49 @@ +import { Element, ElementPosition, Elements, ViewRectInfo, ViewVisibleInfoMap, ViewVisibleInfo } from '@idraw/types'; +import { calcElementOriginRectInfo } from './view-calc'; +import { getGroupQueueByElementPosition } from './element'; + +export function sortElementsViewVisiableInfoMap(elements: Elements): ViewVisibleInfoMap { + const visibleInfoMap: ViewVisibleInfoMap = {}; + const currentPosition: ElementPosition = []; + + const _walk = (elem: Element) => { + const baseInfo: Omit = { + viewRectInfo: null, + rangeRectInfo: null, + isVisibleInView: true, + isGroup: elem.type === 'group', + position: [...currentPosition] + }; + let originRectInfo: ViewRectInfo | null = null; + const groupQueue = getGroupQueueByElementPosition(elements, currentPosition); + originRectInfo = calcElementOriginRectInfo(elem, { + groupQueue: groupQueue || [] + }); + visibleInfoMap[elem.uuid] = { + ...baseInfo, + ...{ + originRectInfo: originRectInfo as ViewRectInfo + } + }; + + if (elem.type === 'group') { + (elem as Element<'group'>).detail.children.forEach((ele, i) => { + if (ele.type === 'group') { + currentPosition.push(i); + } + _walk(ele); + if (ele.type === 'group') { + currentPosition.pop(); + } + }); + } + }; + + elements.forEach((elem, index) => { + currentPosition.push(index); + _walk(elem); + currentPosition.pop(); + }); + + return visibleInfoMap; +}