From 3aba19d5642f5f5f5f1dc4da7c06354ef187731b Mon Sep 17 00:00:00 2001 From: chenshenhai Date: Sun, 11 Jun 2023 13:32:53 +0800 Subject: [PATCH] refactor: refactor util calc --- packages/board/src/index.ts | 12 +- packages/board/src/lib/calculator.ts | 97 +++------- packages/board/src/lib/sharer.ts | 16 +- packages/board/src/lib/viewer.ts | 32 ++-- packages/board/src/lib/watcher.ts | 4 +- packages/core/src/index.ts | 8 +- .../src/middleware/deep-selector/config.ts | 5 +- .../middleware/deep-selector/draw-wrapper.ts | 2 +- .../src/middleware/deep-selector/index.ts | 91 +++++---- .../src/middleware/deep-selector/types.ts | 9 +- .../core/src/middleware/deep-selector/util.ts | 52 ++++-- packages/core/src/middleware/scroller/util.ts | 14 +- .../src/middleware/selector/draw-wrapper.ts | 2 +- .../core/src/middleware/selector/index.ts | 28 +-- packages/core/src/middleware/selector/util.ts | 20 +- packages/renderer/src/draw/circle.ts | 6 +- packages/renderer/src/draw/elements.ts | 2 +- packages/renderer/src/draw/group.ts | 6 +- packages/renderer/src/draw/html.ts | 4 +- packages/renderer/src/draw/image.ts | 4 +- packages/renderer/src/draw/rect.ts | 6 +- packages/renderer/src/draw/svg.ts | 4 +- packages/renderer/src/draw/text.ts | 4 +- packages/renderer/src/index.ts | 8 +- packages/types/src/lib/board.ts | 6 +- packages/types/src/lib/renderer.ts | 8 +- packages/types/src/lib/store.ts | 8 +- packages/types/src/lib/view.ts | 15 +- packages/util/src/index.ts | 1 + packages/util/src/lib/view-calc.ts | 176 ++++++++++++++++++ 30 files changed, 409 insertions(+), 241 deletions(-) create mode 100644 packages/util/src/lib/view-calc.ts diff --git a/packages/board/src/index.ts b/packages/board/src/index.ts index 323b751..4a39198 100644 --- a/packages/board/src/index.ts +++ b/packages/board/src/index.ts @@ -259,21 +259,21 @@ export class Board { } scale(num: number) { - const scaleInfo = this._viewer.scale(num); + const viewScaleInfo = this._viewer.scale(num); this._viewer.drawFrame(); - this._watcher.trigger('scale', scaleInfo); + this._watcher.trigger('scale', viewScaleInfo); } scrollX(num: number) { - const scaleInfo = this._viewer.scrollX(num); + const viewScaleInfo = this._viewer.scrollX(num); this._viewer.drawFrame(); - this._watcher.trigger('scrollX', scaleInfo); + this._watcher.trigger('scrollX', viewScaleInfo); } scrollY(num: number) { - const scaleInfo = this._viewer.scrollY(num); + const viewScaleInfo = this._viewer.scrollY(num); this._viewer.drawFrame(); - this._watcher.trigger('scrollY', scaleInfo); + this._watcher.trigger('scrollY', viewScaleInfo); } resize(newViewSize: ViewSizeInfo) { diff --git a/packages/board/src/lib/calculator.ts b/packages/board/src/lib/calculator.ts index 8b34fa1..1f81616 100644 --- a/packages/board/src/lib/calculator.ts +++ b/packages/board/src/lib/calculator.ts @@ -1,5 +1,14 @@ import type { Data, Point, Element, ElementType, ViewCalculator, ViewCalculatorOptions, ViewScaleInfo, ElementSize, ViewSizeInfo } from '@idraw/types'; -import { rotateElementVertexes, checkRectIntersect } from '@idraw/util'; +import { + rotateElementVertexes, + checkRectIntersect, + viewScale, + viewScroll, + calcElementSize, + isViewPointInElement, + getViewPointAtElement, + isElementInView +} from '@idraw/util'; export class Calculator implements ViewCalculator { private _opts: ViewCalculatorOptions; @@ -8,50 +17,14 @@ export class Calculator implements ViewCalculator { this._opts = opts; } - viewScale(num: number, prevScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): ViewScaleInfo { - const scale = num; - - const { width, height, contextWidth, contextHeight } = viewSizeInfo; - let offsetLeft = 0; - let offsetRight = 0; - let offsetTop = 0; - let offsetBottom = 0; - - if (contextWidth * scale < width) { - offsetLeft = offsetRight = (width - contextWidth * scale) / 2; - } else if (contextWidth * scale > width) { - if (prevScaleInfo.offsetLeft < 0) { - offsetLeft = (prevScaleInfo.offsetLeft / prevScaleInfo.scale) * scale; - offsetRight = 0 - (contextWidth * scale - width - Math.abs(offsetLeft)); - } - } - - if (contextHeight * scale < height) { - offsetTop = offsetBottom = (height - contextHeight * scale) / 2; - } else if (contextHeight * scale > height) { - if (prevScaleInfo.offsetTop < 0) { - offsetTop = (prevScaleInfo.offsetTop / prevScaleInfo.scale) * scale; - offsetBottom = 0 - (contextHeight * scale - height - Math.abs(offsetTop)); - } - } - - return { - scale, - offsetTop, - offsetLeft, - offsetRight, - offsetBottom - }; - } - - viewScroll(opts: { moveX?: number; moveY?: number }, scaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): ViewScaleInfo { - const scale = scaleInfo.scale; + viewScroll(opts: { moveX?: number; moveY?: number }, viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): ViewScaleInfo { + const scale = viewScaleInfo.scale; const { moveX, moveY } = opts; const { width, height, contextWidth, contextHeight } = viewSizeInfo; - let offsetLeft = scaleInfo.offsetLeft; - let offsetRight = scaleInfo.offsetRight; - let offsetTop = scaleInfo.offsetTop; - let offsetBottom = scaleInfo.offsetBottom; + let offsetLeft = viewScaleInfo.offsetLeft; + let offsetRight = viewScaleInfo.offsetRight; + let offsetTop = viewScaleInfo.offsetTop; + let offsetBottom = viewScaleInfo.offsetBottom; if (moveX !== undefined && (moveX > 0 || moveX <= 0)) { if (contextWidth * scale < width) { offsetLeft = offsetRight = (width - contextWidth * scale) / 2; @@ -99,10 +72,10 @@ export class Calculator implements ViewCalculator { }; } - elementSize(size: ElementSize, scaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): ElementSize { + elementSize(size: ElementSize, viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): ElementSize { const { x, y, w, h, angle } = size; const { contextX = 0, contextY = 0 } = viewSizeInfo; - const { scale, offsetTop, offsetLeft } = scaleInfo; + const { scale, offsetTop, offsetLeft } = viewScaleInfo; const newSize = { x: x * scale + offsetLeft - contextX, @@ -113,33 +86,12 @@ export class Calculator implements ViewCalculator { }; return newSize; - - // const { x, y, w, h, angle } = size; - // const { scale, offsetTop, offsetLeft } = scaleInfo; - // return { - // x: x * scale + offsetLeft, - // y: y * scale + offsetTop, - // w: w * scale, - // h: h * scale, - // angle - // }; } - isElementInView(elem: ElementSize, scaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): boolean { - // const { width, height } = viewSizeInfo; - // const { angle } = elem; - // const { x, y, w, h } = this.elementSize(elem, scaleInfo, viewSizeInfo); - // const ves = rotateElementVertexes({ x, y, w, h, angle }); - // for (let i = 0; i < ves.length; i++) { - // const v = ves[i]; - // if (v.x >= 0 && v.x <= width && v.y >= 0 && v.y <= height) { - // return true; - // } - // } - + isElementInView(elem: ElementSize, viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): boolean { const { width, height } = viewSizeInfo; const { angle } = elem; - const { x, y, w, h } = this.elementSize(elem, scaleInfo, viewSizeInfo); + const { x, y, w, h } = this.elementSize(elem, viewScaleInfo, viewSizeInfo); const ves = rotateElementVertexes({ x, y, w, h, angle }); const viewSize = { x: 0, y: 0, w: width, h: height }; @@ -148,14 +100,13 @@ export class Calculator implements ViewCalculator { const elemEndX = Math.max(ves[0].x, ves[1].x, ves[2].x, ves[3].x); const elemEndY = Math.max(ves[0].y, ves[1].y, ves[2].y, ves[3].y); const elemSize = { x: elemStartX, y: elemStartY, w: elemEndX - elemStartX, h: elemEndY - elemStartY }; - return checkRectIntersect(viewSize, elemSize); } - isPointInElement(p: Point, elem: Element, scaleInfo: ViewScaleInfo, viewSize: ViewSizeInfo): boolean { + isPointInElement(p: Point, elem: Element, viewScaleInfo: ViewScaleInfo, viewSize: ViewSizeInfo): boolean { const ctx = this._opts.viewContent.boardContext; const { angle = 0 } = elem; - const { x, y, w, h } = this.elementSize(elem, scaleInfo, viewSize); + const { x, y, w, h } = this.elementSize(elem, viewScaleInfo, viewSize); const vertexes = rotateElementVertexes({ x, y, w, h, angle }); if (vertexes.length >= 2) { ctx.beginPath(); @@ -171,14 +122,14 @@ export class Calculator implements ViewCalculator { return false; } - getPointElement(p: Point, data: Data, scaleInfo: ViewScaleInfo, viewSize: ViewSizeInfo): { index: number; element: null | Element } { + getPointElement(p: Point, data: Data, viewScaleInfo: ViewScaleInfo, viewSize: ViewSizeInfo): { index: number; element: null | Element } { const result: { index: number; element: null | Element } = { index: -1, element: null }; for (let i = data.elements.length - 1; i >= 0; i--) { const elem = data.elements[i]; - if (this.isPointInElement(p, elem, scaleInfo, viewSize)) { + if (this.isPointInElement(p, elem, viewScaleInfo, viewSize)) { result.index = i; result.element = elem; break; diff --git a/packages/board/src/lib/sharer.ts b/packages/board/src/lib/sharer.ts index 4762022..8b3c550 100644 --- a/packages/board/src/lib/sharer.ts +++ b/packages/board/src/lib/sharer.ts @@ -19,10 +19,10 @@ const defaultActiveStorage: ActiveStore = { offsetBottom: 0 }; -export class Sharer implements StoreSharer { +export class Sharer implements StoreSharer> { private _activeStore: Store; private _sharedStore: Store<{ - [string: string]: any; + [string: string | number | symbol]: any; }>; constructor() { @@ -48,11 +48,11 @@ export class Sharer implements StoreSharer { return this._activeStore.getSnapshot(); } - getSharedStorage(key: string): any { + getSharedStorage(key: string | number | symbol): any { return this._sharedStore.get(key); } - setSharedStorage(key: string, storage: any) { + setSharedStorage(key: string | number | symbol, storage: any) { return this._sharedStore.set(key, storage); } @@ -63,18 +63,18 @@ export class Sharer implements StoreSharer { // get/set active info getActiveScaleInfo(): ViewScaleInfo { - const scaleInfo: ViewScaleInfo = { + const viewScaleInfo: ViewScaleInfo = { scale: this._activeStore.get('scale'), offsetTop: this._activeStore.get('offsetTop'), offsetBottom: this._activeStore.get('offsetBottom'), offsetLeft: this._activeStore.get('offsetLeft'), offsetRight: this._activeStore.get('offsetRight') }; - return scaleInfo; + return viewScaleInfo; } - setActiveScaleInfo(scaleInfo: ViewScaleInfo) { - const { scale, offsetTop, offsetBottom, offsetLeft, offsetRight } = scaleInfo; + setActiveScaleInfo(viewScaleInfo: ViewScaleInfo) { + const { scale, offsetTop, offsetBottom, offsetLeft, offsetRight } = viewScaleInfo; this._activeStore.set('scale', scale); this._activeStore.set('offsetTop', offsetTop); this._activeStore.set('offsetBottom', offsetBottom); diff --git a/packages/board/src/lib/viewer.ts b/packages/board/src/lib/viewer.ts index 4dfb73d..ee4d558 100644 --- a/packages/board/src/lib/viewer.ts +++ b/packages/board/src/lib/viewer.ts @@ -1,4 +1,4 @@ -import { EventEmitter } from '@idraw/util'; +import { EventEmitter, viewScale } from '@idraw/util'; import type { BoardViewer, BoardViewerEventMap, BoardViewerOptions, ActiveStore, BoardViewerFrameSnapshot, ViewScaleInfo, ViewSizeInfo } from '@idraw/types'; const { requestAnimationFrame } = window; @@ -40,14 +40,14 @@ export class Viewer extends EventEmitter implements BoardVi if (snapshot?.activeStore.data) { renderer.drawData(snapshot.activeStore.data, { - scaleInfo: { + viewScaleInfo: { scale, offsetTop, offsetBottom, offsetLeft, offsetRight }, - viewSize: { + viewSizeInfo: { width, height, contextX, @@ -90,31 +90,31 @@ export class Viewer extends EventEmitter implements BoardVi } scale(num: number): ViewScaleInfo { - const { sharer, renderer, calculator } = this._opts; - const prevScaleInfo: ViewScaleInfo = sharer.getActiveScaleInfo(); + const { sharer, renderer } = this._opts; + const prevViewScaleInfo: ViewScaleInfo = sharer.getActiveScaleInfo(); const viewSizeInfo: ViewSizeInfo = sharer.getActiveViewSizeInfo(); - const scaleInfo = calculator.viewScale(num, prevScaleInfo, viewSizeInfo); - sharer.setActiveScaleInfo(scaleInfo); + const viewScaleInfo = viewScale(num, { prevViewScaleInfo, viewSizeInfo }); + sharer.setActiveScaleInfo(viewScaleInfo); renderer.scale(num); - return scaleInfo; + return viewScaleInfo; } scrollX(num: number): ViewScaleInfo { const { sharer, calculator } = this._opts; - const prevScaleInfo: ViewScaleInfo = sharer.getActiveScaleInfo(); + const prevViewScaleInfo: ViewScaleInfo = sharer.getActiveScaleInfo(); const viewSizeInfo: ViewSizeInfo = sharer.getActiveViewSizeInfo(); - const scaleInfo = calculator.viewScroll({ moveX: num - (prevScaleInfo.offsetLeft || 0) }, prevScaleInfo, viewSizeInfo); - sharer.setActiveScaleInfo(scaleInfo); - return scaleInfo; + const viewScaleInfo = calculator.viewScroll({ moveX: num - (prevViewScaleInfo.offsetLeft || 0) }, prevViewScaleInfo, viewSizeInfo); + sharer.setActiveScaleInfo(viewScaleInfo); + return viewScaleInfo; } scrollY(num: number): ViewScaleInfo { const { sharer, calculator } = this._opts; - const prevScaleInfo: ViewScaleInfo = sharer.getActiveScaleInfo(); + const prevViewScaleInfo: ViewScaleInfo = sharer.getActiveScaleInfo(); const viewSizeInfo: ViewSizeInfo = sharer.getActiveViewSizeInfo(); - const scaleInfo = calculator.viewScroll({ moveY: num - (prevScaleInfo.offsetTop || 0) }, prevScaleInfo, viewSizeInfo); - sharer.setActiveScaleInfo(scaleInfo); - return scaleInfo; + const viewScaleInfo = calculator.viewScroll({ moveY: num - (prevViewScaleInfo.offsetTop || 0) }, prevViewScaleInfo, viewSizeInfo); + sharer.setActiveScaleInfo(viewScaleInfo); + return viewScaleInfo; } resize(viewSize: Partial = {}): ViewSizeInfo { diff --git a/packages/board/src/lib/watcher.ts b/packages/board/src/lib/watcher.ts index f9aecd8..27e1a2f 100644 --- a/packages/board/src/lib/watcher.ts +++ b/packages/board/src/lib/watcher.ts @@ -134,8 +134,8 @@ export class BoardWatcher extends EventEmitter { } private _isVaildPoint(p: Point): boolean { - const viewSize = this._opts.sharer.getActiveViewSizeInfo(); - const { width, height } = viewSize; + const viewSizeInfo = this._opts.sharer.getActiveViewSizeInfo(); + const { width, height } = viewSizeInfo; if (isBoardAvailableNum(p.x) && isBoardAvailableNum(p.y) && p.x <= width && p.y <= height) { return true; } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 12e8ea3..85201e7 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -76,10 +76,10 @@ export class Core { resize(newViewSize: ViewSizeInfo) { const sharer = this._board.getSharer(); - const scaleInfo = sharer.getActiveScaleInfo(); + const viewScaleInfo = sharer.getActiveScaleInfo(); this._board.resize(newViewSize); - // this._board.scale(scaleInfo.scale); - // this._board.scrollX(scaleInfo.offsetLeft); - // this._board.scrollY(scaleInfo.offsetTop); + // this._board.scale(viewScaleInfo.scale); + // this._board.scrollX(viewScaleInfo.offsetLeft); + // this._board.scrollY(viewScaleInfo.offsetTop); } } diff --git a/packages/core/src/middleware/deep-selector/config.ts b/packages/core/src/middleware/deep-selector/config.ts index 4afee2c..b4dbff1 100644 --- a/packages/core/src/middleware/deep-selector/config.ts +++ b/packages/core/src/middleware/deep-selector/config.ts @@ -4,11 +4,12 @@ export const keyActionType = Symbol(`${key}_actionType`); // 'select' | 'drag-li export const keyResizeType = Symbol(`${key}_resizeType`); // ResizeType | null; export const keyAreaStart = Symbol(`${key}_areaStart`); // Point export const keyAreaEnd = Symbol(`${key}_areaEnd`); // Point -export const keyInGroupQueue = Symbol(`${key}_targetQueue`); // Element<'group'>[] +export const keyGroupQueue = Symbol(`${key}_groupQueue`); // Element<'group'>[] +export const keyInGroup = Symbol(`${key}_inGroup`); // Element<'group'>[] // export const keyHoverElementSize = `${key}_hoverElementSize`; // export const keyActionType = `${key}_actionType`; // 'select' | 'drag-list' | 'drag-list-end' | 'drag' | 'hover' | 'resize' | 'area' | null = null; // export const keyResizeType = `${key}_resizeType`; // ResizeType | null; // export const keyAreaStart = `${key}_areaStart`; // Point // export const keyAreaEnd = `${key}_areaEnd`; // Point -// export const keyInGroupQueue = `${key}_targetQueue`; // Element<'group'>[] +// export const keyGroupQueue = `${key}_targetQueue`; // Element<'group'>[] diff --git a/packages/core/src/middleware/deep-selector/draw-wrapper.ts b/packages/core/src/middleware/deep-selector/draw-wrapper.ts index 4b6de18..ed45847 100644 --- a/packages/core/src/middleware/deep-selector/draw-wrapper.ts +++ b/packages/core/src/middleware/deep-selector/draw-wrapper.ts @@ -101,7 +101,7 @@ export function drawElementListShadows(ctx: ViewContext2D, elements: Element = (opts) => { const { viewer, sharer, viewContent, calculator } = opts; @@ -35,7 +35,7 @@ export const MiddlewareSelector: BoardMiddleware = (o }; const pushGroupQueue = (elem: Element<'group'>) => { - let groupQueue = sharer.getSharedStorage(keyInGroupQueue); + let groupQueue = sharer.getSharedStorage(keyGroupQueue); if (!Array.isArray(groupQueue)) { groupQueue = []; } @@ -48,7 +48,7 @@ export const MiddlewareSelector: BoardMiddleware = (o } else if (groupQueue.length === 0) { groupQueue.push(elem); } - sharer.setSharedStorage(keyInGroupQueue, groupQueue); + sharer.setSharedStorage(keyGroupQueue, groupQueue); return groupQueue.length > 0; }; @@ -58,7 +58,8 @@ export const MiddlewareSelector: BoardMiddleware = (o sharer.setSharedStorage(keyResizeType, null); sharer.setSharedStorage(keyAreaStart, null); sharer.setSharedStorage(keyAreaEnd, null); - sharer.setSharedStorage(keyInGroupQueue, null); + sharer.setSharedStorage(keyGroupQueue, null); + sharer.setSharedStorage(keyInGroup, null); }; clear(); @@ -78,23 +79,23 @@ export const MiddlewareSelector: BoardMiddleware = (o sharer.setSharedStorage(keyHoverElementSize, null); } else if (data) { const selectedElements = getActiveElements(); - const scaleInfo = sharer.getActiveScaleInfo(); - const viewSize = sharer.getActiveViewSizeInfo(); + const viewScaleInfo = sharer.getActiveScaleInfo(); + const viewSizeInfo = sharer.getActiveViewSizeInfo(); const target = getPointTarget(e.point, { ctx: helperContext, data, selectedIndexes: getIndexes(), selectedUUIDs: sharer.getActiveStorage('selectedUUIDs') || [], selectedElements: selectedElements, - scaleInfo, - viewSize, + viewScaleInfo, + viewSizeInfo, calculator, areaSize: calcSelectedElementsArea(selectedElements, { - scaleInfo, - viewSize, + viewScaleInfo, + viewSizeInfo, calculator }), - groupQueue: [] // TODO + groupQueue: sharer.getSharedStorage(keyGroupQueue) }); if (target.type === 'over-element' && target?.elements?.length === 1) { const { x, y, w, h, angle } = target.elements[0]; @@ -116,8 +117,8 @@ export const MiddlewareSelector: BoardMiddleware = (o sharer.setSharedStorage(keyHoverElementSize, null); const data = sharer.getActiveStorage('data'); const listAreaSize = calcSelectedElementsArea(getActiveElements(), { - scaleInfo: sharer.getActiveScaleInfo(), - viewSize: sharer.getActiveViewSizeInfo(), + viewScaleInfo: sharer.getActiveScaleInfo(), + viewSizeInfo: sharer.getActiveViewSizeInfo(), calculator }); const target = getPointTarget(e.point, { @@ -126,13 +127,26 @@ export const MiddlewareSelector: BoardMiddleware = (o selectedIndexes: getIndexes(), selectedUUIDs: sharer.getActiveStorage('selectedUUIDs') || [], selectedElements: getActiveElements(), - scaleInfo: sharer.getActiveScaleInfo(), - viewSize: sharer.getActiveViewSizeInfo(), + viewScaleInfo: sharer.getActiveScaleInfo(), + viewSizeInfo: sharer.getActiveViewSizeInfo(), calculator, areaSize: listAreaSize, - groupQueue: [] // TODO + groupQueue: sharer.getSharedStorage(keyGroupQueue) }); + console.log('pointStart target ====== ', target); + + if (sharer.getSharedStorage(keyInGroup) === true) { + if (target.type === 'in-group-element' && target?.elements?.length > 0) { + // TODO + sharer.setSharedStorage(keyGroupQueue, target.elements as Element<'group'>[]); + return; + } else { + sharer.setSharedStorage(keyInGroup, false); + sharer.setSharedStorage(keyGroupQueue, null); + } + } + if (target.type === 'list-area') { sharer.setSharedStorage(keyActionType, 'drag-list'); } else if (target.type === 'over-element' && target?.uuids?.length === 1 && target?.elements?.length === 1) { @@ -220,9 +234,9 @@ export const MiddlewareSelector: BoardMiddleware = (o const data = sharer.getActiveStorage('data'); const resizeType = sharer.getSharedStorage(keyResizeType); const actionType = sharer.getSharedStorage(keyActionType); - const scaleInfo = sharer.getActiveScaleInfo(); - const viewSize = sharer.getActiveViewSizeInfo(); - const { offsetLeft, offsetTop } = scaleInfo; + const viewScaleInfo = sharer.getActiveScaleInfo(); + const viewSizeInfo = sharer.getActiveViewSizeInfo(); + const { offsetLeft, offsetTop } = viewScaleInfo; let needDrawFrame = false; if (actionType === 'resize' && resizeType) { @@ -237,8 +251,8 @@ export const MiddlewareSelector: BoardMiddleware = (o start, end, calculator, - scaleInfo: sharer.getActiveScaleInfo(), - viewSize: sharer.getActiveViewSizeInfo() + viewScaleInfo: sharer.getActiveScaleInfo(), + viewSizeInfo: sharer.getActiveViewSizeInfo() }); if (uuids.length > 0) { @@ -270,7 +284,7 @@ export const MiddlewareSelector: BoardMiddleware = (o return; } if (data && Array.isArray(data?.elements) && (['drag', 'drag-list'] as ActionType[]).includes(actionType)) { - const viewInfo = calcElementsViewInfo(data.elements, viewSize, { extend: true }); + const viewInfo = calcElementsViewInfo(data.elements, viewSizeInfo, { extend: true }); sharer.setActiveStorage('contextX', viewInfo.contextSize.contextX); sharer.setActiveStorage('contextY', viewInfo.contextSize.contextY); sharer.setActiveStorage('contextHeight', viewInfo.contextSize.contextHeight); @@ -290,8 +304,6 @@ export const MiddlewareSelector: BoardMiddleware = (o }, doubleClick(e: PointWatcherEvent) { - // console.log('doubleClick =====', e); - const groupQueue = sharer.getSharedStorage(keyInGroupQueue); const data = sharer.getActiveStorage('data'); const target = getPointTarget(e.point, { ctx: helperContext, @@ -299,14 +311,18 @@ export const MiddlewareSelector: BoardMiddleware = (o selectedIndexes: getIndexes(), selectedUUIDs: sharer.getActiveStorage('selectedUUIDs') || [], selectedElements: getActiveElements(), - scaleInfo: sharer.getActiveScaleInfo(), - viewSize: sharer.getActiveViewSizeInfo(), + viewScaleInfo: sharer.getActiveScaleInfo(), + viewSizeInfo: sharer.getActiveViewSizeInfo(), calculator, areaSize: null, - groupQueue: [] // TODO + groupQueue: sharer.getSharedStorage(keyGroupQueue) }); - if (target.elements.length === 1 && target.elements[0]?.type === 'group') { + if (target.type === 'in-group-element' && target.elements.length > 0) { + sharer.setSharedStorage(keyGroupQueue, target.elements as Element<'group'>[]); + sharer.setSharedStorage(keyInGroup, true); + } else if (target.elements.length === 1 && target.elements[0]?.type === 'group') { const pushResult = pushGroupQueue(target.elements[0] as Element<'group'>); + sharer.setSharedStorage(keyInGroup, pushResult); if (pushResult === true) { viewer.drawFrame(); return; @@ -334,8 +350,8 @@ export const MiddlewareSelector: BoardMiddleware = (o contextWidth, devicePixelRatio } = activeStore; - const scaleInfo = { scale, offsetLeft, offsetTop, offsetRight, offsetBottom }; - const viewSize = { width, height, contextX, contextY, contextHeight, contextWidth, devicePixelRatio }; + const viewScaleInfo = { scale, offsetLeft, offsetTop, offsetRight, offsetBottom }; + const viewSizeInfo = { width, height, contextX, contextY, contextHeight, contextWidth, devicePixelRatio }; // const elem = data?.elements?.[selectedIndexes?.[0] as number]; const selectedElements = getSelectedElements(data, selectedUUIDs); const elem = selectedElements[0]; @@ -343,21 +359,22 @@ export const MiddlewareSelector: BoardMiddleware = (o const actionType: ActionType = sharedStore[keyActionType] as ActionType; const areaStart: Point | null = sharedStore[keyAreaStart]; const areaEnd: Point | null = sharedStore[keyAreaEnd]; - const groupQueue: Element<'group'>[] | null = sharedStore[keyInGroupQueue]; + const inGroup: boolean | null = sharedStore[keyInGroup]; + const groupQueue: Element<'group'>[] | null = sharedStore[keyGroupQueue]; - if (groupQueue && groupQueue?.length > 0) { + if (inGroup && groupQueue && groupQueue?.length > 0) { // in group drawGroupsWrapper(helperContext, groupQueue); } else { // in root - const drawOpts = { calculator, scaleInfo, viewSize }; + const drawOpts = { calculator, viewScaleInfo, viewSizeInfo }; if (hoverElement && actionType !== 'drag') { - const hoverElemSize = calculator.elementSize(hoverElement, scaleInfo, viewSize); + const hoverElemSize = calculator.elementSize(hoverElement, viewScaleInfo, viewSizeInfo); drawHoverWrapper(helperContext, hoverElemSize); } if (elem && (['select', 'drag', 'resize'] as ActionType[]).includes(actionType)) { - const selectedElemSize = calculator.elementSize(elem, scaleInfo, viewSize); + const selectedElemSize = calculator.elementSize(elem, viewScaleInfo, viewSizeInfo); const sizeControllers = calcElementControllerStyle(selectedElemSize); drawPointWrapper(helperContext, selectedElemSize); drawElementControllers(helperContext, selectedElemSize, { ...drawOpts, sizeControllers }); @@ -365,8 +382,8 @@ export const MiddlewareSelector: BoardMiddleware = (o drawArea(helperContext, { start: areaStart, end: areaEnd }); } else if ((['drag-list', 'drag-list-end'] as ActionType[]).includes(actionType)) { const listAreaSize = calcSelectedElementsArea(getActiveElements(), { - scaleInfo: sharer.getActiveScaleInfo(), - viewSize: sharer.getActiveViewSizeInfo(), + viewScaleInfo: sharer.getActiveScaleInfo(), + viewSizeInfo: sharer.getActiveViewSizeInfo(), calculator }); if (listAreaSize) { diff --git a/packages/core/src/middleware/deep-selector/types.ts b/packages/core/src/middleware/deep-selector/types.ts index 0aff95f..048ba7f 100644 --- a/packages/core/src/middleware/deep-selector/types.ts +++ b/packages/core/src/middleware/deep-selector/types.ts @@ -1,4 +1,4 @@ -import { keyHoverElementSize, keyActionType, keyResizeType, keyAreaStart, keyAreaEnd, keyInGroupQueue } from './config'; +import { keyHoverElementSize, keyActionType, keyResizeType, keyAreaStart, keyAreaEnd, keyGroupQueue, keyInGroup } from './config'; import { Data, @@ -40,7 +40,7 @@ export type ElementSizeController = Record; export type ResizeType = 'resize-left' | 'resize-right' | 'resize-top' | 'resize-bottom'; -export type PointTargetType = null | 'list-area' | 'over-element' | ResizeType; +export type PointTargetType = null | ResizeType | 'list-area' | 'over-element' | 'in-group-element'; export interface PointTarget { type: PointTargetType; @@ -55,9 +55,10 @@ export type ActionType = 'select' | 'drag-list' | 'drag-list-end' | 'drag' | 'ho export type DeepSelectorSharedStorage = { [keyHoverElementSize]: ElementSize | null; - [keyActionType]: ActionType; + [keyActionType]: ActionType | null; [keyResizeType]: ResizeType | null; [keyAreaStart]: Point | null; [keyAreaEnd]: Point | null; - [keyInGroupQueue]: Element<'group'>[] | null; + [keyGroupQueue]: Element<'group'>[] | null; + [keyInGroup]: boolean | null; }; diff --git a/packages/core/src/middleware/deep-selector/util.ts b/packages/core/src/middleware/deep-selector/util.ts index d32b14e..a208922 100644 --- a/packages/core/src/middleware/deep-selector/util.ts +++ b/packages/core/src/middleware/deep-selector/util.ts @@ -38,10 +38,10 @@ export function getPointTarget( selectedUUIDs: Array; selectedElements?: Element[]; areaSize?: AreaSize | null; - scaleInfo: ViewScaleInfo; - viewSize: ViewSizeInfo; + viewScaleInfo: ViewScaleInfo; + viewSizeInfo: ViewSizeInfo; calculator: ViewCalculator; - groupQueue: Element<'group'>[]; + groupQueue: Element<'group'>[] | null; } ): PointTarget { const target: PointTarget = { @@ -50,7 +50,31 @@ export function getPointTarget( indexes: [], uuids: [] }; - const { ctx, data, calculator, selectedElements, selectedIndexes, selectedUUIDs, scaleInfo, viewSize, areaSize } = opts; + const { ctx, data, calculator, selectedElements, selectedIndexes, selectedUUIDs, viewScaleInfo, viewSizeInfo, areaSize, groupQueue } = opts; + + // in-group-element + if (groupQueue && Array.isArray(groupQueue) && groupQueue.length > 0 && data) { + const { index, element } = calculator.getPointElement(p as Point, data, viewScaleInfo, viewSizeInfo); + if (index >= 0 && element) { + const newQueue: Element<'group'>[] = []; + for (let i = 0; i < groupQueue.length; i++) { + const group = groupQueue[i]; + if (group.type !== 'group') { + break; + } + if (element.uuid === group.uuid) { + // isElementInGroup(element, group) + // TODO check in group + newQueue.push(group); + target.indexes = []; + target.elements = newQueue; + target.uuids = []; + target.type = 'in-group-element'; + return target; + } + } + } + } // list area if (areaSize && Array.isArray(selectedElements) && selectedElements?.length > 1 && Array.isArray(selectedIndexes) && selectedIndexes?.length > 1) { @@ -66,7 +90,7 @@ export function getPointTarget( // resize if (selectedElements?.length === 1) { - const elemSize = calculator.elementSize(selectedElements[0], scaleInfo, viewSize); + const elemSize = calculator.elementSize(selectedElements[0], viewScaleInfo, viewSizeInfo); const ctrls = calcElementControllerStyle(elemSize); rotateElement(ctx, elemSize, () => { const ctrlKeys = Object.keys(ctrls); @@ -93,7 +117,7 @@ export function getPointTarget( // over-element if (data) { - const { index, element } = calculator.getPointElement(p as Point, data, scaleInfo, viewSize); + const { index, element } = calculator.getPointElement(p as Point, data, viewScaleInfo, viewSizeInfo); if (index >= 0 && element) { target.indexes = [index]; target.elements = [element]; @@ -383,14 +407,14 @@ export function getSelectedListArea( opts: { start: Point; end: Point; - scaleInfo: ViewScaleInfo; - viewSize: ViewSizeInfo; + viewScaleInfo: ViewScaleInfo; + viewSizeInfo: ViewSizeInfo; calculator: ViewCalculator; } ): { indexes: number[]; uuids: string[] } { const indexes: number[] = []; const uuids: string[] = []; - const { calculator, scaleInfo, viewSize, start, end } = opts; + const { calculator, viewScaleInfo, viewSizeInfo, start, end } = opts; if (!(Array.isArray(data.elements) && start && end)) { return { indexes, uuids }; @@ -401,7 +425,7 @@ export function getSelectedListArea( const endY = Math.max(start.y, end.y); data.elements.forEach((elem, idx) => { - const elemSize = calculator.elementSize(elem, scaleInfo, viewSize); + const elemSize = calculator.elementSize(elem, viewScaleInfo, viewSizeInfo); const center = calcElementCenter(elemSize); if (center.x >= startX && center.x <= endX && center.y >= startY && center.y <= endY) { @@ -426,8 +450,8 @@ export function getSelectedListArea( export function calcSelectedElementsArea( elements: Element[], opts: { - scaleInfo: ViewScaleInfo; - viewSize: ViewSizeInfo; + viewScaleInfo: ViewScaleInfo; + viewSizeInfo: ViewSizeInfo; calculator: ViewCalculator; } ): AreaSize | null { @@ -435,11 +459,11 @@ export function calcSelectedElementsArea( return null; } const area: AreaSize = { x: 0, y: 0, w: 0, h: 0 }; - const { calculator, scaleInfo, viewSize } = opts; + const { calculator, viewScaleInfo, viewSizeInfo } = opts; let prevElemSize: ElementSize | null = null; elements.forEach((elem) => { - const elemSize = calculator.elementSize(elem, scaleInfo, viewSize); + const elemSize = calculator.elementSize(elem, viewScaleInfo, viewSizeInfo); if (elemSize.angle && (elemSize.angle > 0 || elemSize.angle < 0)) { const ves = rotateElementVertexes(elemSize); diff --git a/packages/core/src/middleware/scroller/util.ts b/packages/core/src/middleware/scroller/util.ts index e09c97c..3a22303 100644 --- a/packages/core/src/middleware/scroller/util.ts +++ b/packages/core/src/middleware/scroller/util.ts @@ -48,9 +48,9 @@ export function isPointInScrollbar(helperContext: ViewContext2D, p: Point, sizeI return thumbType; } -export function calcScrollerInfo(scaleInfo: ViewScaleInfo, sizeInfo: ViewSizeInfo) { +export function calcScrollerInfo(viewScaleInfo: ViewScaleInfo, sizeInfo: ViewSizeInfo) { const { width, height } = sizeInfo; - const { offsetTop, offsetBottom, offsetLeft, offsetRight } = scaleInfo; + const { offsetTop, offsetBottom, offsetLeft, offsetRight } = viewScaleInfo; const sliderMinSize = scrollerLineWidth * 2.5; const lineSize = scrollerLineWidth; let xSize = 0; @@ -138,11 +138,11 @@ function drawScrollerThumb( ctx.stroke(); } -function drawScrollerInfo(helperContext: ViewContext2D, opts: { scaleInfo: ViewScaleInfo; sizeInfo: ViewSizeInfo }) { +function drawScrollerInfo(helperContext: ViewContext2D, opts: { viewScaleInfo: ViewScaleInfo; sizeInfo: ViewSizeInfo }) { const ctx = helperContext; - const { scaleInfo, sizeInfo } = opts; + const { viewScaleInfo, sizeInfo } = opts; const { width, height } = sizeInfo; - const wrapper = calcScrollerInfo(scaleInfo, sizeInfo); + const wrapper = calcScrollerInfo(viewScaleInfo, sizeInfo); if (wrapper.xSize > 0) { if (scrollConfig.showBackground === true) { ctx.globalAlpha = scrollerAlpha; @@ -191,6 +191,6 @@ function drawScrollerInfo(helperContext: ViewContext2D, opts: { scaleInfo: ViewS export function drawScroller(ctx: ViewContext2D, opts: { snapshot: BoardViewerFrameSnapshot }) { const { snapshot } = opts; const sizeInfo = getViewSizeInfoFromSnapshot(snapshot); - const scaleInfo = getViewScaleInfoFromSnapshot(snapshot); - drawScrollerInfo(ctx, { sizeInfo, scaleInfo }); + const viewScaleInfo = getViewScaleInfoFromSnapshot(snapshot); + drawScrollerInfo(ctx, { sizeInfo, viewScaleInfo }); } diff --git a/packages/core/src/middleware/selector/draw-wrapper.ts b/packages/core/src/middleware/selector/draw-wrapper.ts index 0a53d23..8170cf4 100644 --- a/packages/core/src/middleware/selector/draw-wrapper.ts +++ b/packages/core/src/middleware/selector/draw-wrapper.ts @@ -101,7 +101,7 @@ export function drawElementListShadows(ctx: ViewContext2D, elements: Element { sharer.setSharedStorage(keyHoverElementSize, null); } else if (data) { const selectedElements = getActiveElements(); - const scaleInfo = sharer.getActiveScaleInfo(); + const viewScaleInfo = sharer.getActiveScaleInfo(); const viewSize = sharer.getActiveViewSizeInfo(); const target = getPointTarget(e.point, { ctx: helperContext, @@ -73,11 +73,11 @@ export const MiddlewareSelector: BoardMiddleware = (opts) => { selectedIndexes: getIndexes(), selectedUUIDs: sharer.getActiveStorage('selectedUUIDs') || [], selectedElements: selectedElements, - scaleInfo, + viewScaleInfo, viewSize, calculator, areaSize: calcSelectedElementsArea(selectedElements, { - scaleInfo, + viewScaleInfo, viewSize, calculator }) @@ -102,7 +102,7 @@ export const MiddlewareSelector: BoardMiddleware = (opts) => { sharer.setSharedStorage(keyHoverElementSize, null); const data = sharer.getActiveStorage('data'); const listAreaSize = calcSelectedElementsArea(getActiveElements(), { - scaleInfo: sharer.getActiveScaleInfo(), + viewScaleInfo: sharer.getActiveScaleInfo(), viewSize: sharer.getActiveViewSizeInfo(), calculator }); @@ -112,7 +112,7 @@ export const MiddlewareSelector: BoardMiddleware = (opts) => { selectedIndexes: getIndexes(), selectedUUIDs: sharer.getActiveStorage('selectedUUIDs') || [], selectedElements: getActiveElements(), - scaleInfo: sharer.getActiveScaleInfo(), + viewScaleInfo: sharer.getActiveScaleInfo(), viewSize: sharer.getActiveViewSizeInfo(), calculator, areaSize: listAreaSize @@ -205,9 +205,9 @@ export const MiddlewareSelector: BoardMiddleware = (opts) => { const data = sharer.getActiveStorage('data'); const resizeType = sharer.getSharedStorage(keyResizeType); const actionType = sharer.getSharedStorage(keyActionType); - const scaleInfo = sharer.getActiveScaleInfo(); + const viewScaleInfo = sharer.getActiveScaleInfo(); const viewSize = sharer.getActiveViewSizeInfo(); - const { scale, offsetLeft, offsetTop } = scaleInfo; + const { scale, offsetLeft, offsetTop } = viewScaleInfo; const { width, height, contextHeight, contextWidth, contextX, contextY } = viewSize; let needDrawFrame = false; @@ -222,7 +222,7 @@ export const MiddlewareSelector: BoardMiddleware = (opts) => { start, end, calculator, - scaleInfo: sharer.getActiveScaleInfo(), + viewScaleInfo: sharer.getActiveScaleInfo(), viewSize: sharer.getActiveViewSizeInfo() }); @@ -291,8 +291,8 @@ export const MiddlewareSelector: BoardMiddleware = (opts) => { contextWidth, devicePixelRatio } = activeStore; - const scaleInfo = { scale, offsetLeft, offsetTop, offsetRight, offsetBottom }; - const viewSize = { width, height, contextX, contextY, contextHeight, contextWidth, devicePixelRatio }; + const viewScaleInfo = { scale, offsetLeft, offsetTop, offsetRight, offsetBottom }; + const viewSizeInfo = { width, height, contextX, contextY, contextHeight, contextWidth, devicePixelRatio }; // const elem = data?.elements?.[selectedIndexes?.[0] as number]; const selectedElements = getSelectedElements(data, selectedUUIDs); const elem = selectedElements[0]; @@ -301,14 +301,14 @@ export const MiddlewareSelector: BoardMiddleware = (opts) => { const areaStart: Point | null = sharedStore[keyAreaStart]; const areaEnd: Point | null = sharedStore[keyAreaEnd]; - const drawOpts = { calculator, scaleInfo, viewSize }; + const drawOpts = { calculator, viewScaleInfo, viewSizeInfo }; if (hoverElement && actionType !== 'drag') { - const hoverElemSize = calculator.elementSize(hoverElement, scaleInfo, viewSize); + const hoverElemSize = calculator.elementSize(hoverElement, viewScaleInfo, viewSizeInfo); drawHoverWrapper(helperContext, hoverElemSize); } if (elem && ['select', 'drag', 'resize'].includes(actionType)) { - const selectedElemSize = calculator.elementSize(elem, scaleInfo, viewSize); + const selectedElemSize = calculator.elementSize(elem, viewScaleInfo, viewSizeInfo); const sizeControllers = calcElementControllerStyle(selectedElemSize); drawPointWrapper(helperContext, selectedElemSize); drawElementControllers(helperContext, selectedElemSize, { ...drawOpts, sizeControllers }); @@ -316,7 +316,7 @@ export const MiddlewareSelector: BoardMiddleware = (opts) => { drawArea(helperContext, { start: areaStart, end: areaEnd }); } else if (['drag-list', 'drag-list-end'].includes(actionType)) { const listAreaSize = calcSelectedElementsArea(getActiveElements(), { - scaleInfo: sharer.getActiveScaleInfo(), + viewScaleInfo: sharer.getActiveScaleInfo(), viewSize: sharer.getActiveViewSizeInfo(), calculator }); diff --git a/packages/core/src/middleware/selector/util.ts b/packages/core/src/middleware/selector/util.ts index 6e50106..0b203e1 100644 --- a/packages/core/src/middleware/selector/util.ts +++ b/packages/core/src/middleware/selector/util.ts @@ -38,7 +38,7 @@ export function getPointTarget( selectedUUIDs: Array; selectedElements?: Element[]; areaSize?: AreaSize | null; - scaleInfo: ViewScaleInfo; + viewScaleInfo: ViewScaleInfo; viewSize: ViewSizeInfo; calculator: ViewCalculator; } @@ -49,7 +49,7 @@ export function getPointTarget( indexes: [], uuids: [] }; - const { ctx, data, calculator, selectedElements, selectedIndexes, selectedUUIDs, scaleInfo, viewSize, areaSize } = opts; + const { ctx, data, calculator, selectedElements, selectedIndexes, selectedUUIDs, viewScaleInfo, viewSize, areaSize } = opts; // list area if (areaSize && Array.isArray(selectedElements) && selectedElements?.length > 1 && Array.isArray(selectedIndexes) && selectedIndexes?.length > 1) { @@ -65,7 +65,7 @@ export function getPointTarget( // resize if (selectedElements?.length === 1) { - const elemSize = calculator.elementSize(selectedElements[0], scaleInfo, viewSize); + const elemSize = calculator.elementSize(selectedElements[0], viewScaleInfo, viewSize); const ctrls = calcElementControllerStyle(elemSize); rotateElement(ctx, elemSize, () => { const ctrlKeys = Object.keys(ctrls); @@ -92,7 +92,7 @@ export function getPointTarget( // over-element if (data) { - const { index, element } = calculator.getPointElement(p as Point, data, scaleInfo, viewSize); + const { index, element } = calculator.getPointElement(p as Point, data, viewScaleInfo, viewSize); if (index >= 0 && element) { target.indexes = [index]; target.elements = [element]; @@ -382,14 +382,14 @@ export function getSelectedListArea( opts: { start: Point; end: Point; - scaleInfo: ViewScaleInfo; + viewScaleInfo: ViewScaleInfo; viewSize: ViewSizeInfo; calculator: ViewCalculator; } ): { indexes: number[]; uuids: string[] } { const indexes: number[] = []; const uuids: string[] = []; - const { calculator, scaleInfo, viewSize, start, end } = opts; + const { calculator, viewScaleInfo, viewSize, start, end } = opts; if (!(Array.isArray(data.elements) && start && end)) { return { indexes, uuids }; @@ -400,7 +400,7 @@ export function getSelectedListArea( const endY = Math.max(start.y, end.y); data.elements.forEach((elem, idx) => { - const elemSize = calculator.elementSize(elem, scaleInfo, viewSize); + const elemSize = calculator.elementSize(elem, viewScaleInfo, viewSize); const center = calcElementCenter(elemSize); if (center.x >= startX && center.x <= endX && center.y >= startY && center.y <= endY) { @@ -425,7 +425,7 @@ export function getSelectedListArea( export function calcSelectedElementsArea( elements: Element[], opts: { - scaleInfo: ViewScaleInfo; + viewScaleInfo: ViewScaleInfo; viewSize: ViewSizeInfo; calculator: ViewCalculator; } @@ -434,11 +434,11 @@ export function calcSelectedElementsArea( return null; } const area: AreaSize = { x: 0, y: 0, w: 0, h: 0 }; - const { calculator, scaleInfo, viewSize } = opts; + const { calculator, viewScaleInfo, viewSize } = opts; let prevElemSize: ElementSize | null = null; elements.forEach((elem) => { - const elemSize = calculator.elementSize(elem, scaleInfo, viewSize); + const elemSize = calculator.elementSize(elem, viewScaleInfo, viewSize); if (elemSize.angle && (elemSize.angle > 0 || elemSize.angle < 0)) { const ves = rotateElementVertexes(elemSize); diff --git a/packages/renderer/src/draw/circle.ts b/packages/renderer/src/draw/circle.ts index 8b9c99a..56c8b5d 100644 --- a/packages/renderer/src/draw/circle.ts +++ b/packages/renderer/src/draw/circle.ts @@ -4,9 +4,9 @@ import { rotateElement } from '@idraw/util'; export function drawCircle(ctx: ViewContext2D, elem: Element<'circle'>, opts: RendererDrawElementOptions) { const { desc, angle } = elem; const { bgColor = '#000000', borderColor = '#000000', borderWidth = 0 } = desc; - const { calculator, scaleInfo, viewSize } = opts; - // const { scale, offsetTop, offsetBottom, offsetLeft, offsetRight } = scaleInfo; - const { x, y, w, h } = calculator.elementSize({ x: elem.x, y: elem.y, w: elem.w, h: elem.h }, scaleInfo, viewSize); + const { calculator, viewScaleInfo, viewSizeInfo } = opts; + // const { scale, offsetTop, offsetBottom, offsetLeft, offsetRight } = viewScaleInfo; + const { x, y, w, h } = calculator.elementSize({ x: elem.x, y: elem.y, w: elem.w, h: elem.h }, viewScaleInfo, viewSizeInfo); rotateElement(ctx, { x, y, w, h, angle }, () => { const a = w / 2; const b = h / 2; diff --git a/packages/renderer/src/draw/elements.ts b/packages/renderer/src/draw/elements.ts index 31fd13f..cad5a18 100644 --- a/packages/renderer/src/draw/elements.ts +++ b/packages/renderer/src/draw/elements.ts @@ -51,7 +51,7 @@ export function drawElementList(ctx: ViewContext2D, elements: Data['elements'], for (let i = 0; i < elements.length; i++) { const elem = elements[i]; // TODO - if (!opts.calculator.isElementInView(elem, opts.scaleInfo, opts.viewSize)) { + if (!opts.calculator.isElementInView(elem, opts.viewScaleInfo, opts.viewSizeInfo)) { continue; } try { diff --git a/packages/renderer/src/draw/group.ts b/packages/renderer/src/draw/group.ts index f203877..616a05b 100644 --- a/packages/renderer/src/draw/group.ts +++ b/packages/renderer/src/draw/group.ts @@ -49,8 +49,8 @@ export function drawElement(ctx: ViewContext2D, elem: Element, opts } export function drawGroup(ctx: ViewContext2D, elem: Element<'group'>, opts: RendererDrawElementOptions) { - const { calculator, scaleInfo, viewSize } = opts; - const { x, y, w, h, angle } = calculator.elementSize({ x: elem.x, y: elem.y, w: elem.w, h: elem.h, angle: elem.angle }, scaleInfo, viewSize); + const { calculator, viewScaleInfo, viewSizeInfo } = opts; + const { x, y, w, h, angle } = calculator.elementSize({ x: elem.x, y: elem.y, w: elem.w, h: elem.h, angle: elem.angle }, viewScaleInfo, viewSizeInfo); rotateElement(ctx, { x, y, w, h, angle }, () => { drawBox(ctx, { ...elem, ...{ x, y, w, h, angle } }, elem?.desc?.bgColor); @@ -83,7 +83,7 @@ export function drawGroup(ctx: ViewContext2D, elem: Element<'group'>, opts: Rend y: newParentSize.y + child.y } }; - if (!calculator.isElementInView(child, opts.scaleInfo, opts.viewSize)) { + if (!calculator.isElementInView(child, opts.viewScaleInfo, opts.viewSizeInfo)) { continue; } try { diff --git a/packages/renderer/src/draw/html.ts b/packages/renderer/src/draw/html.ts index b922e0a..dd9bb14 100644 --- a/packages/renderer/src/draw/html.ts +++ b/packages/renderer/src/draw/html.ts @@ -3,8 +3,8 @@ import { rotateElement } from '@idraw/util'; export function drawHTML(ctx: ViewContext2D, elem: Element<'html'>, opts: RendererDrawElementOptions) { const content = opts.loader.getContent(elem.uuid); - const { calculator, scaleInfo, viewSize } = opts; - const { x, y, w, h, angle } = calculator.elementSize(elem, scaleInfo, viewSize); + const { calculator, viewScaleInfo, viewSizeInfo } = opts; + const { x, y, w, h, angle } = calculator.elementSize(elem, viewScaleInfo, viewSizeInfo); rotateElement(ctx, { x, y, w, h, angle }, () => { if (!content) { opts.loader.load(elem as Element<'html'>); diff --git a/packages/renderer/src/draw/image.ts b/packages/renderer/src/draw/image.ts index 89e0794..5e1ff28 100644 --- a/packages/renderer/src/draw/image.ts +++ b/packages/renderer/src/draw/image.ts @@ -3,8 +3,8 @@ import { rotateElement } from '@idraw/util'; export function drawImage(ctx: ViewContext2D, elem: Element<'image'>, opts: RendererDrawElementOptions) { const content = opts.loader.getContent(elem.uuid); - const { calculator, scaleInfo, viewSize } = opts; - const { x, y, w, h, angle } = calculator.elementSize(elem, scaleInfo, viewSize); + const { calculator, viewScaleInfo, viewSizeInfo } = opts; + const { x, y, w, h, angle } = calculator.elementSize(elem, viewScaleInfo, viewSizeInfo); rotateElement(ctx, { x, y, w, h, angle }, () => { if (!content) { opts.loader.load(elem as Element<'image'>); diff --git a/packages/renderer/src/draw/rect.ts b/packages/renderer/src/draw/rect.ts index 2fa7a54..adc29f2 100644 --- a/packages/renderer/src/draw/rect.ts +++ b/packages/renderer/src/draw/rect.ts @@ -3,11 +3,11 @@ import { rotateElement } from '@idraw/util'; export function drawRect(ctx: ViewContext2D, elem: Element<'rect'>, opts: RendererDrawElementOptions) { // const { desc } = elem; - const { calculator, scaleInfo, viewSize } = opts; + const { calculator, viewScaleInfo, viewSizeInfo } = opts; - const { x, y, w, h, angle } = calculator.elementSize(elem, scaleInfo, viewSize); + const { x, y, w, h, angle } = calculator.elementSize(elem, viewScaleInfo, viewSizeInfo); rotateElement(ctx, { x, y, w, h, angle }, () => { - let r: number = (elem.desc.borderRadius || 0) * scaleInfo.scale; + let r: number = (elem.desc.borderRadius || 0) * viewScaleInfo.scale; r = Math.min(r, w / 2, h / 2); if (w < r * 2 || h < r * 2) { r = 0; diff --git a/packages/renderer/src/draw/svg.ts b/packages/renderer/src/draw/svg.ts index d8c8841..c176b4c 100644 --- a/packages/renderer/src/draw/svg.ts +++ b/packages/renderer/src/draw/svg.ts @@ -3,8 +3,8 @@ import { rotateElement } from '@idraw/util'; export function drawSVG(ctx: ViewContext2D, elem: Element<'svg'>, opts: RendererDrawElementOptions) { const content = opts.loader.getContent(elem.uuid); - const { calculator, scaleInfo, viewSize } = opts; - const { x, y, w, h, angle } = calculator.elementSize(elem, scaleInfo, viewSize); + const { calculator, viewScaleInfo, viewSizeInfo } = opts; + const { x, y, w, h, angle } = calculator.elementSize(elem, viewScaleInfo, viewSizeInfo); rotateElement(ctx, { x, y, w, h, angle }, () => { if (!content) { opts.loader.load(elem as Element<'svg'>); diff --git a/packages/renderer/src/draw/text.ts b/packages/renderer/src/draw/text.ts index 081645a..53fc9b6 100644 --- a/packages/renderer/src/draw/text.ts +++ b/packages/renderer/src/draw/text.ts @@ -4,8 +4,8 @@ import { is, isColorStr } from '@idraw/util'; import { drawBox } from './base'; export function drawText(ctx: ViewContext2D, elem: Element<'text'>, opts: RendererDrawElementOptions) { - const { calculator, scaleInfo, viewSize } = opts; - const { x, y, w, h, angle } = calculator.elementSize(elem, scaleInfo, viewSize); + const { calculator, viewScaleInfo, viewSizeInfo } = opts; + const { x, y, w, h, angle } = calculator.elementSize(elem, viewScaleInfo, viewSizeInfo); rotateElement(ctx, { x, y, w, h, angle }, () => { drawBox(ctx, { ...elem, ...{ x, y, w, h, angle } }, elem.desc.bgColor || 'transparent'); const desc: Element<'text'>['desc'] = { diff --git a/packages/renderer/src/index.ts b/packages/renderer/src/index.ts index f60f1d5..50817a8 100644 --- a/packages/renderer/src/index.ts +++ b/packages/renderer/src/index.ts @@ -43,8 +43,8 @@ export class Renderer extends EventEmitter implements BoardRen const parentElementSize = { x: 0, y: 0, - w: opts.viewSize.width, - h: opts.viewSize.height + w: opts.viewSizeInfo.width, + h: opts.viewSizeInfo.height }; drawElementList(viewContext, data.elements, { loader, @@ -60,14 +60,14 @@ export class Renderer extends EventEmitter implements BoardRen sharer.getActiveStoreSnapshot(); if (data) { this.drawData(data, { - scaleInfo: { + viewScaleInfo: { scale: num, offsetTop, offsetBottom, offsetLeft, offsetRight }, - viewSize: { + viewSizeInfo: { width, height, contextX, diff --git a/packages/types/src/lib/board.ts b/packages/types/src/lib/board.ts index 1f1a146..26c6576 100644 --- a/packages/types/src/lib/board.ts +++ b/packages/types/src/lib/board.ts @@ -30,7 +30,7 @@ export type BoardWatherScrollYEvent = ViewScaleInfo; export type BoardWatherResizeEvent = ViewSizeInfo; -export interface BoardWatcherEventMap> { +export interface BoardWatcherEventMap = any> { hover: BoardWatcherPointEvent; pointStart: BoardWatcherPointEvent; pointMove: BoardWatcherPointEvent; @@ -49,7 +49,7 @@ export interface BoardWatcherEventMap> { export type BoardMode = 'SELECT' | 'SCROLL' | 'RULE' | 'CONNECT' | 'PENCIL' | 'PEN' | string; -export interface BoardMiddlewareObject> { +export interface BoardMiddlewareObject = any> { mode: BoardMode; isDefault?: boolean; created?: () => void; @@ -86,7 +86,7 @@ export interface BoardOptions { viewContent: ViewContent; } -export interface BoardViewerFrameSnapshot> { +export interface BoardViewerFrameSnapshot = any> { activeStore: ActiveStore; sharedStore: S; } diff --git a/packages/types/src/lib/renderer.ts b/packages/types/src/lib/renderer.ts index c0594b5..25c7ad9 100644 --- a/packages/types/src/lib/renderer.ts +++ b/packages/types/src/lib/renderer.ts @@ -24,14 +24,14 @@ export interface RendererLoader extends UtilEventEmitter { } export interface RendererDrawOptions { - viewSize: ViewSizeInfo; - scaleInfo: ViewScaleInfo; + viewSizeInfo: ViewSizeInfo; + viewScaleInfo: ViewScaleInfo; } export interface RendererDrawElementOptions extends RendererDrawOptions { loader: RendererLoader; calculator: ViewCalculator; - scaleInfo: ViewScaleInfo; - viewSize: ViewSizeInfo; + viewScaleInfo: ViewScaleInfo; + viewSizeInfo: ViewSizeInfo; parentElementSize: ElementSize; } diff --git a/packages/types/src/lib/store.ts b/packages/types/src/lib/store.ts index 30fec45..54ca02a 100644 --- a/packages/types/src/lib/store.ts +++ b/packages/types/src/lib/store.ts @@ -8,16 +8,16 @@ export type ActiveStore = ViewSizeInfo & selectedUUIDs: string[]; }; -export interface StoreSharer> { +export interface StoreSharer = any> { getActiveStorage(key: T): ActiveStore[T]; setActiveStorage(key: T, storage: ActiveStore[T]): void; getActiveStoreSnapshot(): ActiveStore; - getSharedStorage(key: K): S[K]; - setSharedStorage(key: K, storage: S[K]): void; + getSharedStorage(key: K): S[K]; + setSharedStorage(key: K, storage: S[K]): void; getSharedStoreSnapshot(): Record; getActiveScaleInfo(): ViewScaleInfo; - setActiveScaleInfo(scaleInfo: ViewScaleInfo): void; + setActiveScaleInfo(viewScaleInfo: ViewScaleInfo): void; setActiveViewSizeInfo(size: ViewSizeInfo): void; getActiveViewSizeInfo(): ViewSizeInfo; } diff --git a/packages/types/src/lib/view.ts b/packages/types/src/lib/view.ts index fac6e33..133437a 100644 --- a/packages/types/src/lib/view.ts +++ b/packages/types/src/lib/view.ts @@ -35,13 +35,10 @@ export interface ViewCalculatorOptions { } export interface ViewCalculator { - viewScale(num: number, prevScaleInfo: ViewScaleInfo, viewSize: ViewSizeInfo): ViewScaleInfo; - isElementInView(elem: Element, scaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): boolean; - isPointInElement(p: Point, elem: Element, scaleInfo: ViewScaleInfo, viewSize: ViewSizeInfo): boolean; - elementSize(size: ElementSize, scaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): ElementSize; - viewScroll(opts: { moveX?: number; moveY?: number }, scaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): ViewScaleInfo; - getPointElement(p: Point, data: Data, scaleInfo: ViewScaleInfo, viewSize: ViewSizeInfo): { index: number; element: null | Element }; - // rotateElementSize(elemSize: ElementSize): PointSize[]; - // pointToViewPoint( p: Point): Point; - // TODO + // viewScale(num: number, prevScaleInfo: ViewScaleInfo, viewSize: ViewSizeInfo): ViewScaleInfo; + isElementInView(elem: Element, viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): boolean; + isPointInElement(p: Point, elem: Element, viewScaleInfo: ViewScaleInfo, viewSize: ViewSizeInfo): boolean; + elementSize(size: ElementSize, viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): ElementSize; + viewScroll(opts: { moveX?: number; moveY?: number }, viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): ViewScaleInfo; + getPointElement(p: Point, data: Data, viewScaleInfo: ViewScaleInfo, viewSize: ViewSizeInfo): { index: number; element: null | Element }; } diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts index eafbaf7..372f410 100644 --- a/packages/util/src/index.ts +++ b/packages/util/src/index.ts @@ -23,3 +23,4 @@ export { calcElementsViewInfo } from './lib/element'; export { checkRectIntersect } from './lib/rect'; +export { viewScale, viewScroll, calcElementSize, isViewPointInElement, getViewPointAtElement, isElementInView } from './lib/view-calc'; diff --git a/packages/util/src/lib/view-calc.ts b/packages/util/src/lib/view-calc.ts new file mode 100644 index 0000000..802f4d0 --- /dev/null +++ b/packages/util/src/lib/view-calc.ts @@ -0,0 +1,176 @@ +import { Point, Data, ViewScaleInfo, ViewSizeInfo, Element, ElementType, ElementSize } from '@idraw/types'; +import { rotateElementVertexes } from './rotate'; +import { Context2D } from './context2d'; +import { checkRectIntersect } from './rect'; + +export function viewScale(num: number, opts: { prevViewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): ViewScaleInfo { + const scale = num; + const { prevViewScaleInfo, viewSizeInfo } = opts; + const { width, height, contextWidth, contextHeight } = viewSizeInfo; + let offsetLeft = 0; + let offsetRight = 0; + let offsetTop = 0; + let offsetBottom = 0; + + if (contextWidth * scale < width) { + offsetLeft = offsetRight = (width - contextWidth * scale) / 2; + } else if (contextWidth * scale > width) { + if (prevViewScaleInfo.offsetLeft < 0) { + offsetLeft = (prevViewScaleInfo.offsetLeft / prevViewScaleInfo.scale) * scale; + offsetRight = 0 - (contextWidth * scale - width - Math.abs(offsetLeft)); + } + } + + if (contextHeight * scale < height) { + offsetTop = offsetBottom = (height - contextHeight * scale) / 2; + } else if (contextHeight * scale > height) { + if (prevViewScaleInfo.offsetTop < 0) { + offsetTop = (prevViewScaleInfo.offsetTop / prevViewScaleInfo.scale) * scale; + offsetBottom = 0 - (contextHeight * scale - height - Math.abs(offsetTop)); + } + } + + return { + scale, + offsetTop, + offsetLeft, + offsetRight, + offsetBottom + }; +} + +export function viewScroll(opts: { moveX?: number; moveY?: number }, viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): ViewScaleInfo { + const scale = viewScaleInfo.scale; + const { moveX, moveY } = opts; + const { width, height, contextWidth, contextHeight } = viewSizeInfo; + let offsetLeft = viewScaleInfo.offsetLeft; + let offsetRight = viewScaleInfo.offsetRight; + let offsetTop = viewScaleInfo.offsetTop; + let offsetBottom = viewScaleInfo.offsetBottom; + if (moveX !== undefined && (moveX > 0 || moveX <= 0)) { + if (contextWidth * scale < width) { + offsetLeft = offsetRight = (width - contextWidth * scale) / 2; + } else if (contextWidth * scale > width) { + if (offsetLeft + moveX >= 0) { + offsetLeft = 0; + offsetRight = width - contextWidth * scale; + } else if (offsetLeft + moveX < width - contextWidth * scale) { + offsetLeft = width - contextWidth * scale; + offsetRight = 0; + } else { + offsetLeft += moveX; + offsetRight = width - contextWidth * scale - offsetLeft; + } + } else { + offsetLeft = offsetRight = 0; + } + } + + if (moveY !== undefined && (moveY > 0 || moveY <= 0)) { + if (contextHeight * scale < height) { + offsetTop = offsetBottom = (height - contextHeight * scale) / 2; + } else if (contextHeight * scale > height) { + if (offsetTop + moveY >= 0) { + offsetTop = 0; + offsetBottom = height - contextHeight * scale; + } else if (offsetTop + moveY < height - contextHeight * scale) { + offsetTop = height - contextHeight * scale; + offsetBottom = 0; + } else { + offsetTop += moveY; + offsetBottom = height - contextHeight * scale - offsetTop; + } + } else { + offsetTop = offsetBottom = 0; + } + } + + return { + scale, + offsetTop, + offsetLeft, + offsetRight, + offsetBottom + }; +} + +export function calcElementSize(size: ElementSize, opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): ElementSize { + const { viewScaleInfo, viewSizeInfo } = opts; + const { x, y, w, h, angle } = size; + const { contextX = 0, contextY = 0 } = viewSizeInfo; + const { scale, offsetTop, offsetLeft } = viewScaleInfo; + + const newSize = { + x: x * scale + offsetLeft - contextX, + y: y * scale + offsetTop - contextY, + w: w * scale, + h: h * scale, + angle + }; + + return newSize; +} + +export function isViewPointInElement( + p: Point, + opts: { context2d: Context2D; element: Element; viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo } +): boolean { + const { context2d: ctx, element: elem, viewScaleInfo, viewSizeInfo } = opts; + + const { angle = 0 } = elem; + const { x, y, w, h } = calcElementSize(elem, { viewScaleInfo, viewSizeInfo }); + const vertexes = rotateElementVertexes({ x, y, w, h, angle }); + if (vertexes.length >= 2) { + ctx.beginPath(); + ctx.moveTo(vertexes[0].x, vertexes[0].y); + for (let i = 1; i < vertexes.length; i++) { + ctx.lineTo(vertexes[i].x, vertexes[i].y); + } + ctx.closePath(); + } + if (ctx.isPointInPath(p.x, p.y)) { + return true; + } + return false; +} + +export function getViewPointAtElement( + p: Point, + opts: { + context2d: Context2D; + data: Data; + viewScaleInfo: ViewScaleInfo; + viewSizeInfo: ViewSizeInfo; + } +): { index: number; element: null | Element } { + const { context2d: ctx, data, viewScaleInfo, viewSizeInfo } = opts; + const result: { index: number; element: null | Element } = { + index: -1, + element: null + }; + for (let i = data.elements.length - 1; i >= 0; i--) { + const elem = data.elements[i]; + if (isViewPointInElement(p, { context2d: ctx, element: elem, viewScaleInfo, viewSizeInfo })) { + result.index = i; + result.element = elem; + break; + } + } + return result; +} + +export function isElementInView(elem: ElementSize, opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): boolean { + const { viewSizeInfo, viewScaleInfo } = opts; + const { width, height } = viewSizeInfo; + const { angle } = elem; + const { x, y, w, h } = calcElementSize(elem, { viewScaleInfo, viewSizeInfo }); + const ves = rotateElementVertexes({ x, y, w, h, angle }); + const viewSize = { x: 0, y: 0, w: width, h: height }; + + const elemStartX = Math.min(ves[0].x, ves[1].x, ves[2].x, ves[3].x); + const elemStartY = Math.min(ves[0].y, ves[1].y, ves[2].y, ves[3].y); + const elemEndX = Math.max(ves[0].x, ves[1].x, ves[2].x, ves[3].x); + const elemEndY = Math.max(ves[0].y, ves[1].y, ves[2].y, ves[3].y); + const elemSize = { x: elemStartX, y: elemStartY, w: elemEndX - elemStartX, h: elemEndY - elemStartY }; + return checkRectIntersect(viewSize, elemSize); +}