refactor: refactor mode of idraw

This commit is contained in:
chenshenhai 2024-03-10 17:10:41 +08:00
parent a5059f51c1
commit 79f380aef2
18 changed files with 597 additions and 159 deletions

View file

@ -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<T extends BoardExtendEventMap = BoardExtendEventMap> {
#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));

View file

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

View file

@ -30,3 +30,5 @@ export const lockColor = '#5b5959b5';
export const controllerSize = 10;
// export const wrapperColor = '#1890ff';
export const auxiliaryColor = '#f7276e';

View file

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

View file

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

View file

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

View file

@ -220,6 +220,19 @@ export const MiddlewareTextEditor: BoardMiddleware<ExtendEventMap, CoreEventMap
hideTextArea();
});
textarea.addEventListener('keydown', (e) => {
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') {

View file

@ -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<IDrawSettings> = {
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;
}

View file

@ -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<IDrawEvent>;
#opts: IDrawOptions;
#store: Store<IDrawStorage> = new Store<IDrawStorage>({
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() {

View file

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

View file

@ -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<IDrawEvent>, store: Store<IDrawStorage>) {
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<IDrawEvent>, store: Store<IDrawStorage>) {
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);
}

View file

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

View file

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

View file

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

View file

@ -293,6 +293,23 @@ export function getGroupQueueFromList(uuid: string, elements: Element<ElementTyp
return groupQueue;
}
export function getGroupQueueByElementPosition(elements: Element<ElementType>[], 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 };

View file

@ -18,7 +18,8 @@ export class Store<T extends Record<string | symbol, any> = Record<string | symb
}
getSnapshot(): T {
return deepClone(this.#temp);
// return deepClone(this.#temp);
return this.#temp;
}
clear() {

View file

@ -1,6 +1,21 @@
import { Point, PointSize, Data, ViewScaleInfo, ViewSizeInfo, Element, ElementType, ElementSize, ViewContext2D, ViewRectVertexes } from '@idraw/types';
import {
Point,
PointSize,
Data,
ViewScaleInfo,
ViewSizeInfo,
Element,
ElementType,
ElementSize,
ViewContext2D,
ViewRectVertexes,
ViewRectInfo,
ViewRectInfoMap
} from '@idraw/types';
import { rotateElementVertexes } from './rotate';
import { checkRectIntersect } from './rect';
import { calcElementVertexesInGroup } from './vertex';
import { getCenterFromTwoPoints } from './point';
export function calcViewScaleInfo(info: { scale: number; offsetX: number; offsetY: number }, opts: { viewSizeInfo: ViewSizeInfo }): ViewScaleInfo {
const { scale, offsetX, offsetY } = info;
@ -83,7 +98,7 @@ export function calcViewElementSize(size: ElementSize, opts: { viewScaleInfo: Vi
return newSize;
}
export function calcViewPointSize(size: PointSize, opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): PointSize {
export function calcViewPointSize(size: PointSize, opts: { viewScaleInfo: ViewScaleInfo }): PointSize {
const { viewScaleInfo } = opts;
const { x, y } = size;
const { scale, offsetTop, offsetLeft } = viewScaleInfo;
@ -224,3 +239,171 @@ export function isElementInView(elem: ElementSize, opts: { viewScaleInfo: ViewSc
const elemSize = { x: elemStartX, y: elemStartY, w: elemEndX - elemStartX, h: elemEndY - elemStartY };
return checkRectIntersect(viewSize, elemSize);
}
export function calcElementOriginRectInfo(
elemSize: ElementSize,
opts: {
groupQueue: Element<'group'>[];
}
): 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
};
}

View file

@ -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<ViewVisibleInfo, 'originRectInfo'> = {
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;
}