mirror of
https://github.com/idrawjs/idraw
synced 2026-05-24 01:58:27 +00:00
feat: add info middleware
This commit is contained in:
parent
591a9294ae
commit
378f9c4cbf
28 changed files with 550 additions and 111 deletions
|
|
@ -6,7 +6,6 @@ import type {
|
|||
ViewCalculator,
|
||||
ViewCalculatorOptions,
|
||||
ViewScaleInfo,
|
||||
ElementSize,
|
||||
ViewSizeInfo,
|
||||
ViewCalculatorStorage,
|
||||
ViewRectInfo,
|
||||
|
|
@ -17,7 +16,6 @@ import {
|
|||
is,
|
||||
isViewPointInElement,
|
||||
getViewPointAtElement,
|
||||
isElementInView,
|
||||
Store,
|
||||
sortElementsViewVisiableInfoMap,
|
||||
updateViewVisibleInfoMapStatus,
|
||||
|
|
@ -43,7 +41,10 @@ export class Calculator implements ViewCalculator {
|
|||
});
|
||||
}
|
||||
|
||||
toGridNum(num: number): number {
|
||||
toGridNum(num: number, opts?: { ignore?: boolean }): number {
|
||||
if (opts?.ignore === true) {
|
||||
return num;
|
||||
}
|
||||
// TODO
|
||||
// const gridUnitSize = 1; // px;
|
||||
return Math.round(num);
|
||||
|
|
@ -53,10 +54,18 @@ export class Calculator implements ViewCalculator {
|
|||
this.#opts = null as any;
|
||||
}
|
||||
|
||||
isElementInView(elem: ElementSize, viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): boolean {
|
||||
return isElementInView(elem, { viewScaleInfo, viewSizeInfo });
|
||||
needRender(elem: Element<ElementType>): boolean {
|
||||
const viewVisibleInfoMap = this.#store.get('viewVisibleInfoMap');
|
||||
const info = viewVisibleInfoMap[elem.uuid];
|
||||
if (!info) {
|
||||
return true;
|
||||
}
|
||||
return info.isVisibleInView;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
isPointInElement(p: Point, elem: Element<ElementType>, viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): boolean {
|
||||
const context2d = this.#opts.viewContext;
|
||||
return isViewPointInElement(p, {
|
||||
|
|
|
|||
|
|
@ -102,6 +102,8 @@ export class Viewer extends EventEmitter<BoardViewerEventMap> implements BoardVi
|
|||
const { sharer } = this.#opts;
|
||||
const activeStore: ActiveStore = sharer.getActiveStoreSnapshot();
|
||||
const sharedStore: Record<string, any> = sharer.getSharedStoreSnapshot();
|
||||
// const activeStore: ActiveStore = sharer.getActiveStoreSnapshot({ deepClone: true });
|
||||
// const sharedStore: Record<string, any> = sharer.getSharedStoreSnapshot({ deepClone: true });
|
||||
this.#drawFrameSnapshotQueue.push({
|
||||
activeStore,
|
||||
sharedStore
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ export { MiddlewareScaler, middlewareEventScale } from './middleware/scaler';
|
|||
export { MiddlewareRuler, middlewareEventRuler } from './middleware/ruler';
|
||||
export { MiddlewareTextEditor, middlewareEventTextEdit, middlewareEventTextChange } from './middleware/text-editor';
|
||||
export { MiddlewareDragger } from './middleware/dragger';
|
||||
export { MiddlewareInfo } from './middleware/info';
|
||||
|
||||
export class Core<E extends CoreEventMap = CoreEventMap> {
|
||||
#board: Board<E>;
|
||||
|
|
|
|||
130
packages/core/src/middleware/info/draw-info.ts
Normal file
130
packages/core/src/middleware/info/draw-info.ts
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
import type { PointSize, ViewContext2D } from '@idraw/types';
|
||||
import { rotateByCenter } from '@idraw/util';
|
||||
|
||||
const fontFamily = 'monospace';
|
||||
|
||||
export function drawSizeInfoText(
|
||||
ctx: ViewContext2D,
|
||||
opts: { point: PointSize; rotateCenter: PointSize; angle: number; text: string; fontSize: number; lineHeight: number; color: string; background: string }
|
||||
) {
|
||||
const { point, rotateCenter, angle, text, color, background, fontSize, lineHeight } = opts;
|
||||
|
||||
rotateByCenter(ctx, angle, rotateCenter, () => {
|
||||
ctx.$setFont({
|
||||
fontWeight: '300',
|
||||
fontSize,
|
||||
fontFamily
|
||||
});
|
||||
const padding = (lineHeight - fontSize) / 2;
|
||||
const textWidth = ctx.$undoPixelRatio(ctx.measureText(text).width);
|
||||
const bgStart = {
|
||||
x: point.x - textWidth / 2 - padding,
|
||||
y: point.y
|
||||
};
|
||||
const bgEnd = {
|
||||
x: bgStart.x + textWidth + padding * 2,
|
||||
y: bgStart.y + fontSize + padding
|
||||
};
|
||||
const textStart = {
|
||||
x: point.x - textWidth / 2,
|
||||
y: point.y
|
||||
};
|
||||
ctx.setLineDash([]);
|
||||
ctx.fillStyle = background;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(bgStart.x, bgStart.y);
|
||||
ctx.lineTo(bgEnd.x, bgStart.y);
|
||||
ctx.lineTo(bgEnd.x, bgEnd.y);
|
||||
ctx.lineTo(bgStart.x, bgEnd.y);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
ctx.fillStyle = color;
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.fillText(text, textStart.x, textStart.y + padding);
|
||||
});
|
||||
}
|
||||
|
||||
export function drawPositionInfoText(
|
||||
ctx: ViewContext2D,
|
||||
opts: { point: PointSize; rotateCenter: PointSize; angle: number; text: string; fontSize: number; lineHeight: number; color: string; background: string }
|
||||
) {
|
||||
const { point, rotateCenter, angle, text, color, background, fontSize, lineHeight } = opts;
|
||||
|
||||
rotateByCenter(ctx, angle, rotateCenter, () => {
|
||||
ctx.$setFont({
|
||||
fontWeight: '300',
|
||||
fontSize,
|
||||
fontFamily
|
||||
});
|
||||
const padding = (lineHeight - fontSize) / 2;
|
||||
const textWidth = ctx.$undoPixelRatio(ctx.measureText(text).width);
|
||||
const bgStart = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
};
|
||||
const bgEnd = {
|
||||
x: bgStart.x + textWidth + padding * 2,
|
||||
y: bgStart.y + fontSize + padding
|
||||
};
|
||||
const textStart = {
|
||||
x: point.x + padding,
|
||||
y: point.y
|
||||
};
|
||||
ctx.setLineDash([]);
|
||||
ctx.fillStyle = background;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(bgStart.x, bgStart.y);
|
||||
ctx.lineTo(bgEnd.x, bgStart.y);
|
||||
ctx.lineTo(bgEnd.x, bgEnd.y);
|
||||
ctx.lineTo(bgStart.x, bgEnd.y);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
ctx.fillStyle = color;
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.fillText(text, textStart.x, textStart.y + padding);
|
||||
});
|
||||
}
|
||||
|
||||
export function drawAngleInfoText(
|
||||
ctx: ViewContext2D,
|
||||
opts: { point: PointSize; rotateCenter: PointSize; angle: number; text: string; fontSize: number; lineHeight: number; color: string; background: string }
|
||||
) {
|
||||
const { point, rotateCenter, angle, text, color, background, fontSize, lineHeight } = opts;
|
||||
|
||||
rotateByCenter(ctx, angle, rotateCenter, () => {
|
||||
ctx.$setFont({
|
||||
fontWeight: '300',
|
||||
fontSize,
|
||||
fontFamily
|
||||
});
|
||||
const padding = (lineHeight - fontSize) / 2;
|
||||
const textWidth = ctx.$undoPixelRatio(ctx.measureText(text).width);
|
||||
const bgStart = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
};
|
||||
const bgEnd = {
|
||||
x: bgStart.x + textWidth + padding * 2,
|
||||
y: bgStart.y + fontSize + padding
|
||||
};
|
||||
const textStart = {
|
||||
x: point.x + padding,
|
||||
y: point.y
|
||||
};
|
||||
ctx.setLineDash([]);
|
||||
ctx.fillStyle = background;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(bgStart.x, bgStart.y);
|
||||
ctx.lineTo(bgEnd.x, bgStart.y);
|
||||
ctx.lineTo(bgEnd.x, bgEnd.y);
|
||||
ctx.lineTo(bgStart.x, bgEnd.y);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
ctx.fillStyle = color;
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.fillText(text, textStart.x, textStart.y + padding);
|
||||
});
|
||||
}
|
||||
140
packages/core/src/middleware/info/index.ts
Normal file
140
packages/core/src/middleware/info/index.ts
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
import type { BoardMiddleware, ViewRectInfo, Element } from '@idraw/types';
|
||||
import { formatNumber, getViewScaleInfoFromSnapshot, getViewSizeInfoFromSnapshot, createUUID, limitAngle, rotatePoint, parseAngleToRadian } from '@idraw/util';
|
||||
import { keySelectedElementList, keyActionType, keyGroupQueue } from '../selector';
|
||||
import { drawSizeInfoText, drawPositionInfoText, drawAngleInfoText } from './draw-info';
|
||||
import type { DeepInfoSharedStorage } from './types';
|
||||
|
||||
const infoBackground = '#1973bac6';
|
||||
const infoTextColor = '#ffffff';
|
||||
const infoFontSize = 10;
|
||||
const infoLineHeight = 16;
|
||||
|
||||
export const MiddlewareInfo: BoardMiddleware<DeepInfoSharedStorage> = (opts) => {
|
||||
const { boardContent, calculator } = opts;
|
||||
const { helperContext } = boardContent;
|
||||
|
||||
return {
|
||||
name: '@middleware/info',
|
||||
|
||||
beforeDrawFrame({ snapshot }) {
|
||||
const { sharedStore } = snapshot;
|
||||
|
||||
const selectedElementList = sharedStore[keySelectedElementList];
|
||||
const actionType = sharedStore[keyActionType];
|
||||
const groupQueue = sharedStore[keyGroupQueue] || [];
|
||||
|
||||
// console.log('resizeType ===== ', resizeType);
|
||||
|
||||
if (selectedElementList.length === 1) {
|
||||
const elem = selectedElementList[0];
|
||||
if (elem && ['select', 'drag', 'resize'].includes(actionType as string)) {
|
||||
const viewScaleInfo = getViewScaleInfoFromSnapshot(snapshot);
|
||||
const viewSizeInfo = getViewSizeInfoFromSnapshot(snapshot);
|
||||
const { x, y, w, h, angle } = elem;
|
||||
const totalGroupQueue = [
|
||||
...groupQueue,
|
||||
...[
|
||||
{
|
||||
uuid: createUUID(),
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
h,
|
||||
angle,
|
||||
type: 'group',
|
||||
detail: { children: [] }
|
||||
} as Element<'group'>
|
||||
]
|
||||
];
|
||||
|
||||
const calcOpts = { viewScaleInfo, viewSizeInfo };
|
||||
|
||||
const rangeRectInfo = calculator.calcViewRectInfoFromOrigin(elem.uuid, calcOpts);
|
||||
let totalAngle = 0;
|
||||
totalGroupQueue.forEach((group) => {
|
||||
totalAngle += group.angle || 0;
|
||||
});
|
||||
const totalRadian = parseAngleToRadian(limitAngle(0 - totalAngle));
|
||||
|
||||
if (rangeRectInfo) {
|
||||
const elemCenter = rangeRectInfo?.center;
|
||||
const rectInfo: ViewRectInfo = {
|
||||
topLeft: rotatePoint(elemCenter, rangeRectInfo.topLeft, totalRadian),
|
||||
topRight: rotatePoint(elemCenter, rangeRectInfo.topRight, totalRadian),
|
||||
bottomRight: rotatePoint(elemCenter, rangeRectInfo.bottomRight, totalRadian),
|
||||
bottomLeft: rotatePoint(elemCenter, rangeRectInfo.bottomLeft, totalRadian),
|
||||
center: rotatePoint(elemCenter, rangeRectInfo.center, totalRadian),
|
||||
top: rotatePoint(elemCenter, rangeRectInfo.top, totalRadian),
|
||||
right: rotatePoint(elemCenter, rangeRectInfo.right, totalRadian),
|
||||
bottom: rotatePoint(elemCenter, rangeRectInfo.bottom, totalRadian),
|
||||
left: rotatePoint(elemCenter, rangeRectInfo.left, totalRadian)
|
||||
};
|
||||
|
||||
const x = formatNumber(elem.x, { decimalPlaces: 2 });
|
||||
const y = formatNumber(elem.y, { decimalPlaces: 2 });
|
||||
const w = formatNumber(elem.w, { decimalPlaces: 2 });
|
||||
const h = formatNumber(elem.h, { decimalPlaces: 2 });
|
||||
|
||||
// // test start ----
|
||||
// const ctx = helperContext;
|
||||
// ctx.beginPath();
|
||||
// ctx.moveTo(rectInfo.topLeft.x, rectInfo.topLeft.y);
|
||||
// ctx.lineTo(rectInfo.topRight.x, rectInfo.topRight.y);
|
||||
// ctx.lineTo(rectInfo.bottomRight.x, rectInfo.bottomRight.y);
|
||||
// ctx.lineTo(rectInfo.bottomLeft.x, rectInfo.bottomLeft.y);
|
||||
// ctx.closePath();
|
||||
// ctx.strokeStyle = 'red';
|
||||
// ctx.stroke();
|
||||
// // test end ----
|
||||
|
||||
const xyText = `${formatNumber(x, { decimalPlaces: 0 })},${formatNumber(y, { decimalPlaces: 0 })}`;
|
||||
const whText = `${formatNumber(w, { decimalPlaces: 0 })}x${formatNumber(h, { decimalPlaces: 0 })}`;
|
||||
const angleText = `${formatNumber(elem.angle || 0, { decimalPlaces: 0 })}°`;
|
||||
|
||||
drawSizeInfoText(helperContext, {
|
||||
point: {
|
||||
x: rectInfo.bottom.x,
|
||||
y: rectInfo.bottom.y + infoFontSize
|
||||
},
|
||||
rotateCenter: rectInfo.center,
|
||||
angle: totalAngle,
|
||||
text: whText,
|
||||
fontSize: infoFontSize,
|
||||
lineHeight: infoLineHeight,
|
||||
color: infoTextColor,
|
||||
background: infoBackground
|
||||
});
|
||||
|
||||
drawPositionInfoText(helperContext, {
|
||||
point: {
|
||||
x: rectInfo.topLeft.x,
|
||||
y: rectInfo.topLeft.y - infoFontSize * 2
|
||||
},
|
||||
rotateCenter: rectInfo.center,
|
||||
angle: totalAngle,
|
||||
text: xyText,
|
||||
fontSize: infoFontSize,
|
||||
lineHeight: infoLineHeight,
|
||||
color: infoTextColor,
|
||||
background: infoBackground
|
||||
});
|
||||
|
||||
drawAngleInfoText(helperContext, {
|
||||
point: {
|
||||
x: rectInfo.top.x + infoFontSize,
|
||||
y: rectInfo.top.y - infoFontSize * 2
|
||||
},
|
||||
rotateCenter: rectInfo.center,
|
||||
angle: totalAngle,
|
||||
text: angleText,
|
||||
fontSize: infoFontSize,
|
||||
lineHeight: infoLineHeight,
|
||||
color: infoTextColor,
|
||||
background: infoBackground
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
4
packages/core/src/middleware/info/types.ts
Normal file
4
packages/core/src/middleware/info/types.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import { keySelectedElementList, keyActionType, keyGroupQueue } from '../selector';
|
||||
import type { DeepSelectorSharedStorage } from '../selector';
|
||||
|
||||
export type DeepInfoSharedStorage = Pick<DeepSelectorSharedStorage, typeof keySelectedElementList | typeof keyActionType | typeof keyGroupQueue>;
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
import type { BoardMiddleware, CoreEventMap } from '@idraw/types';
|
||||
import { getViewScaleInfoFromSnapshot, getViewSizeInfoFromSnapshot } from '@idraw/util';
|
||||
import { drawRulerBackground, drawXRuler, drawYRuler, calcXRulerScaleList, calcYRulerScaleList, drawUnderGrid } from './util';
|
||||
import { drawRulerBackground, drawXRuler, drawYRuler, calcXRulerScaleList, calcYRulerScaleList, drawUnderGrid, drawScrollerSelectedArea } from './util';
|
||||
import type { DeepRulerSharedStorage } from './types';
|
||||
|
||||
export const middlewareEventRuler = '@middleware/show-ruler';
|
||||
|
||||
export const MiddlewareRuler: BoardMiddleware<Record<string, any>, CoreEventMap> = (opts) => {
|
||||
const { boardContent, viewer, eventHub } = opts;
|
||||
export const MiddlewareRuler: BoardMiddleware<DeepRulerSharedStorage, CoreEventMap> = (opts) => {
|
||||
const { boardContent, viewer, eventHub, calculator } = opts;
|
||||
const { helperContext, underContext } = boardContent;
|
||||
let show: boolean = true;
|
||||
let showGrid: boolean = true;
|
||||
|
|
@ -35,6 +36,8 @@ export const MiddlewareRuler: BoardMiddleware<Record<string, any>, CoreEventMap>
|
|||
if (show === true) {
|
||||
const viewScaleInfo = getViewScaleInfoFromSnapshot(snapshot);
|
||||
const viewSizeInfo = getViewSizeInfoFromSnapshot(snapshot);
|
||||
drawScrollerSelectedArea(helperContext, { snapshot, calculator });
|
||||
|
||||
drawRulerBackground(helperContext, { viewScaleInfo, viewSizeInfo });
|
||||
|
||||
const xList = calcXRulerScaleList({ viewScaleInfo, viewSizeInfo });
|
||||
|
|
|
|||
4
packages/core/src/middleware/ruler/types.ts
Normal file
4
packages/core/src/middleware/ruler/types.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import { keySelectedElementList, keyActionType } from '../selector';
|
||||
import type { DeepSelectorSharedStorage } from '../selector';
|
||||
|
||||
export type DeepRulerSharedStorage = Pick<DeepSelectorSharedStorage, typeof keySelectedElementList | typeof keyActionType>;
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
import type { ViewScaleInfo, ViewSizeInfo, ViewContext2D } from '@idraw/types';
|
||||
import { formatNumber, rotateByCenter } from '@idraw/util';
|
||||
import type { Element, ViewScaleInfo, ViewSizeInfo, ViewContext2D, BoardViewerFrameSnapshot, ViewRectInfo, ViewCalculator } from '@idraw/types';
|
||||
import { formatNumber, rotateByCenter, getViewScaleInfoFromSnapshot, getViewSizeInfoFromSnapshot } from '@idraw/util';
|
||||
import type { DeepRulerSharedStorage } from './types';
|
||||
import { keySelectedElementList, keyActionType } from '../selector';
|
||||
|
||||
const rulerSize = 16;
|
||||
const background = '#FFFFFFA8';
|
||||
|
|
@ -12,6 +14,7 @@ const fontWeight = 100;
|
|||
const gridColor = '#AAAAAA20';
|
||||
const gridKeyColor = '#AAAAAA40';
|
||||
const lineSize = 1;
|
||||
const selectedAreaColor = '#196097';
|
||||
|
||||
// const rulerUnit = 10;
|
||||
// const rulerKeyUnit = 100;
|
||||
|
|
@ -235,3 +238,62 @@ export function drawUnderGrid(
|
|||
}
|
||||
// TODO
|
||||
}
|
||||
|
||||
export function drawScrollerSelectedArea(ctx: ViewContext2D, opts: { snapshot: BoardViewerFrameSnapshot<DeepRulerSharedStorage>; calculator: ViewCalculator }) {
|
||||
const { snapshot, calculator } = opts;
|
||||
const { sharedStore } = snapshot;
|
||||
const selectedElementList = sharedStore[keySelectedElementList];
|
||||
const actionType = sharedStore[keyActionType];
|
||||
|
||||
if (['select', 'drag', 'drag-list', 'drag-list-end'].includes(actionType as string) && selectedElementList.length > 0) {
|
||||
const viewScaleInfo = getViewScaleInfoFromSnapshot(snapshot);
|
||||
const viewSizeInfo = getViewSizeInfoFromSnapshot(snapshot);
|
||||
const rangeRectInfoList: ViewRectInfo[] = [];
|
||||
const xAreaStartList: number[] = [];
|
||||
const xAreaEndList: number[] = [];
|
||||
const yAreaStartList: number[] = [];
|
||||
const yAreaEndList: number[] = [];
|
||||
selectedElementList.forEach((elem: Element) => {
|
||||
const rectInfo = calculator.calcViewRectInfoFromRange(elem.uuid, {
|
||||
viewScaleInfo,
|
||||
viewSizeInfo
|
||||
});
|
||||
if (rectInfo) {
|
||||
rangeRectInfoList.push(rectInfo);
|
||||
xAreaStartList.push(rectInfo.left.x);
|
||||
xAreaEndList.push(rectInfo.right.x);
|
||||
yAreaStartList.push(rectInfo.top.y);
|
||||
yAreaEndList.push(rectInfo.bottom.y);
|
||||
}
|
||||
});
|
||||
|
||||
if (!(rangeRectInfoList.length > 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const xAreaStart = Math.min(...xAreaStartList);
|
||||
const xAreaEnd = Math.max(...xAreaEndList);
|
||||
const yAreaStart = Math.min(...yAreaStartList);
|
||||
const yAreaEnd = Math.max(...yAreaEndList);
|
||||
|
||||
ctx.globalAlpha = 1;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(xAreaStart, 0);
|
||||
ctx.lineTo(xAreaEnd, 0);
|
||||
ctx.lineTo(xAreaEnd, rulerSize);
|
||||
ctx.lineTo(xAreaStart, rulerSize);
|
||||
ctx.fillStyle = selectedAreaColor;
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, yAreaStart);
|
||||
ctx.lineTo(rulerSize, yAreaStart);
|
||||
ctx.lineTo(rulerSize, yAreaEnd);
|
||||
ctx.lineTo(0, yAreaEnd);
|
||||
ctx.fillStyle = selectedAreaColor;
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ import type { Point, BoardMiddleware, PointWatcherEvent, BoardWatherWheelEvent }
|
|||
import { drawScroller, isPointInScrollThumb } from './util';
|
||||
// import type { ScrollbarThumbType } from './util';
|
||||
import { keyXThumbRect, keyYThumbRect, keyPrevPoint, keyActivePoint, keyActiveThumbType } from './config';
|
||||
import type { DeepScrollerSharedStorage } from './types';
|
||||
|
||||
export const MiddlewareScroller: BoardMiddleware = (opts) => {
|
||||
export const MiddlewareScroller: BoardMiddleware<DeepScrollerSharedStorage> = (opts) => {
|
||||
const { viewer, boardContent, sharer } = opts;
|
||||
const { helperContext } = boardContent;
|
||||
sharer.setSharedStorage(keyXThumbRect, null); // null | ElementSize
|
||||
|
|
|
|||
10
packages/core/src/middleware/scroller/types.ts
Normal file
10
packages/core/src/middleware/scroller/types.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import type { Point, ElementSize } from '@idraw/types';
|
||||
import { keyXThumbRect, keyYThumbRect, keyPrevPoint, keyActivePoint, keyActiveThumbType } from './config';
|
||||
|
||||
export type DeepScrollerSharedStorage = {
|
||||
[keyXThumbRect]: null | ElementSize;
|
||||
[keyYThumbRect]: null | ElementSize;
|
||||
[keyPrevPoint]: null | Point;
|
||||
[keyActivePoint]: null | Point;
|
||||
[keyActiveThumbType]: null | 'X' | 'Y';
|
||||
};
|
||||
|
|
@ -4,7 +4,7 @@ import { keyActivePoint, keyActiveThumbType, keyPrevPoint, keyXThumbRect, keyYTh
|
|||
|
||||
const minScrollerWidth = 12;
|
||||
const scrollerLineWidth = 16;
|
||||
const scrollerThumbAlpha = 0.36;
|
||||
const scrollerThumbAlpha = 0.3;
|
||||
|
||||
export type ScrollbarThumbType = 'X' | 'Y';
|
||||
|
||||
|
|
|
|||
|
|
@ -71,6 +71,9 @@ import {
|
|||
import { calcReferenceInfo } from './reference';
|
||||
import { middlewareEventTextEdit } from '../text-editor';
|
||||
|
||||
export { keySelectedElementList, keyActionType, keyResizeType, keyGroupQueue };
|
||||
export type { DeepSelectorSharedStorage };
|
||||
|
||||
export const middlewareEventSelect: string = '@middleware/select';
|
||||
|
||||
export const middlewareEventSelectClear: string = '@middleware/select-clear';
|
||||
|
|
@ -445,6 +448,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
|
|||
sharer.setSharedStorage(keySelectedReferenceYLines, referenceInfo.yLines);
|
||||
}
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
|
|
@ -479,7 +483,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
|
|||
type: 'updateElement',
|
||||
content: {
|
||||
element: elem,
|
||||
position: sharer.getSharedStorage(keySelectedElementPosition) || []
|
||||
position: getElementPositionFromList(elem.uuid, data.elements) || []
|
||||
}
|
||||
},
|
||||
viewSizeInfo,
|
||||
|
|
@ -487,6 +491,10 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
|
|||
});
|
||||
}
|
||||
});
|
||||
// calculator.updateVisiableStatus({
|
||||
// viewSizeInfo,
|
||||
// viewScaleInfo
|
||||
// });
|
||||
|
||||
sharer.setActiveStorage('data', data);
|
||||
}
|
||||
|
|
@ -533,22 +541,20 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
|
|||
sharer
|
||||
});
|
||||
|
||||
elems[0].angle = resizedElemSize.angle;
|
||||
elems[0].angle = calculator.toGridNum(resizedElemSize.angle || 0);
|
||||
} else {
|
||||
const resizedElemSize = resizeElement(elems[0], { scale, start: resizeStart, end: resizeEnd, resizeType, sharer });
|
||||
elems[0].x = calculator.toGridNum(resizedElemSize.x);
|
||||
elems[0].y = calculator.toGridNum(resizedElemSize.y);
|
||||
const calcOpts = { ignore: !!elems[0].angle };
|
||||
elems[0].x = calculator.toGridNum(resizedElemSize.x, calcOpts);
|
||||
elems[0].y = calculator.toGridNum(resizedElemSize.y, calcOpts);
|
||||
if (elems[0].type === 'group' && elems[0].operations?.deepResize === true) {
|
||||
// TODO
|
||||
// elems[0].w = resizedElemSize.w;
|
||||
// elems[0].h = resizedElemSize.h;
|
||||
deepResizeGroupElement(elems[0] as Element<'group'>, {
|
||||
w: calculator.toGridNum(resizedElemSize.w),
|
||||
h: calculator.toGridNum(resizedElemSize.h)
|
||||
w: calculator.toGridNum(resizedElemSize.w, calcOpts),
|
||||
h: calculator.toGridNum(resizedElemSize.h, calcOpts)
|
||||
});
|
||||
} else {
|
||||
elems[0].w = calculator.toGridNum(resizedElemSize.w);
|
||||
elems[0].h = calculator.toGridNum(resizedElemSize.h);
|
||||
elems[0].w = calculator.toGridNum(resizedElemSize.w, calcOpts);
|
||||
elems[0].h = calculator.toGridNum(resizedElemSize.h, calcOpts);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ export function getDefaultStorage(): IDrawStorage {
|
|||
enableScroll: false,
|
||||
enableSelect: false,
|
||||
enableTextEdit: false,
|
||||
enableDrag: false
|
||||
enableDrag: false,
|
||||
enableInfo: false
|
||||
};
|
||||
return storage;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,11 +58,12 @@ export class iDraw {
|
|||
|
||||
#setFeature(feat: IDrawFeature, status: boolean) {
|
||||
const store = this.#store;
|
||||
if (['ruler', 'scroll', 'scale'].includes(feat)) {
|
||||
if (['ruler', 'scroll', 'scale', 'info'].includes(feat)) {
|
||||
const map: Record<IDrawFeature, keyof Omit<IDrawStorage, 'mode'>> = {
|
||||
ruler: 'enableRuler',
|
||||
scroll: 'enableScroll',
|
||||
scale: 'enableScale'
|
||||
scale: 'enableScale',
|
||||
info: 'enableInfo'
|
||||
};
|
||||
store.set(map[feat], !!status);
|
||||
runMiddlewares(this.#core, store);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
import type { IDrawMode, IDrawStorage } from '@idraw/types';
|
||||
import { Store } from '@idraw/util';
|
||||
import { Core, MiddlewareSelector, MiddlewareScroller, MiddlewareScaler, MiddlewareRuler, MiddlewareTextEditor, MiddlewareDragger } from '@idraw/core';
|
||||
import {
|
||||
Core,
|
||||
MiddlewareSelector,
|
||||
MiddlewareScroller,
|
||||
MiddlewareScaler,
|
||||
MiddlewareRuler,
|
||||
MiddlewareTextEditor,
|
||||
MiddlewareDragger,
|
||||
MiddlewareInfo
|
||||
} from '@idraw/core';
|
||||
import { IDrawEvent } from './event';
|
||||
|
||||
function isValidMode(mode: string | IDrawMode) {
|
||||
|
|
@ -8,7 +17,7 @@ function isValidMode(mode: string | IDrawMode) {
|
|||
}
|
||||
|
||||
export function runMiddlewares(core: Core<IDrawEvent>, store: Store<IDrawStorage>) {
|
||||
const { enableRuler, enableScale, enableScroll, enableSelect, enableTextEdit, enableDrag } = store.getSnapshot();
|
||||
const { enableRuler, enableScale, enableScroll, enableSelect, enableTextEdit, enableDrag, enableInfo } = store.getSnapshot();
|
||||
if (enableScroll === true) {
|
||||
core.use(MiddlewareScroller);
|
||||
} else if (enableScroll === false) {
|
||||
|
|
@ -44,6 +53,12 @@ export function runMiddlewares(core: Core<IDrawEvent>, store: Store<IDrawStorage
|
|||
} else if (enableDrag === false) {
|
||||
core.disuse(MiddlewareDragger);
|
||||
}
|
||||
|
||||
if (enableInfo === true) {
|
||||
core.use(MiddlewareInfo);
|
||||
} else if (enableInfo === false) {
|
||||
core.disuse(MiddlewareInfo);
|
||||
}
|
||||
}
|
||||
|
||||
export function changeMode(mode: IDrawMode, core: Core<IDrawEvent>, store: Store<IDrawStorage>) {
|
||||
|
|
@ -53,6 +68,7 @@ export function changeMode(mode: IDrawMode, core: Core<IDrawEvent>, store: Store
|
|||
let enableTextEdit: boolean = false;
|
||||
let enableDrag: boolean = false;
|
||||
let enableRuler: boolean = false;
|
||||
const enableInfo: boolean = true;
|
||||
|
||||
let innerMode: IDrawMode = 'select';
|
||||
store.set('mode', innerMode);
|
||||
|
|
@ -92,6 +108,7 @@ export function changeMode(mode: IDrawMode, core: Core<IDrawEvent>, store: Store
|
|||
store.set('enableTextEdit', enableTextEdit);
|
||||
store.set('enableDrag', enableDrag);
|
||||
store.set('enableRuler', enableRuler);
|
||||
store.set('enableInfo', enableInfo);
|
||||
|
||||
runMiddlewares(core, store);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ export function getOpacity(elem: Element): number {
|
|||
|
||||
export function drawBox(
|
||||
ctx: ViewContext2D,
|
||||
viewElem: Element<ElementType>,
|
||||
viewElem: Element,
|
||||
opts: {
|
||||
originElem: Element<ElementType>;
|
||||
originElem: Element;
|
||||
calcElemSize: ElementSize;
|
||||
pattern?: string | CanvasPattern | null;
|
||||
renderContent: () => void;
|
||||
|
|
@ -88,7 +88,7 @@ function drawClipPath(
|
|||
}
|
||||
}
|
||||
|
||||
function drawBoxBackground(
|
||||
export function drawBoxBackground(
|
||||
ctx: ViewContext2D,
|
||||
viewElem: Element<ElementType>,
|
||||
opts: { pattern?: string | CanvasPattern | null; viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }
|
||||
|
|
@ -149,7 +149,7 @@ function drawBoxBackground(
|
|||
}
|
||||
}
|
||||
|
||||
function drawBoxBorder(ctx: ViewContext2D, viewElem: Element<ElementType>, opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): void {
|
||||
export function drawBoxBorder(ctx: ViewContext2D, viewElem: Element<ElementType>, opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): void {
|
||||
if (viewElem.detail.borderWidth === 0) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export function drawElementList(ctx: ViewContext2D, data: Data, opts: RendererDr
|
|||
}
|
||||
};
|
||||
if (opts.forceDrawAll !== true) {
|
||||
if (!opts.calculator?.isElementInView(elem, opts.viewScaleInfo, opts.viewSizeInfo)) {
|
||||
if (!opts.calculator?.needRender(elem)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ export function drawGroup(ctx: ViewContext2D, elem: Element<'group'>, opts: Rend
|
|||
}
|
||||
};
|
||||
if (opts.forceDrawAll !== true) {
|
||||
if (!calculator?.isElementInView(child, opts.viewScaleInfo, opts.viewSizeInfo)) {
|
||||
if (!calculator?.needRender(child)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ export { drawSVG } from './svg';
|
|||
export { drawHTML } from './html';
|
||||
export { drawText } from './text';
|
||||
export { drawElementList } from './elements';
|
||||
export { drawUnderlay } from './underlay';
|
||||
export { drawLayout } from './layout';
|
||||
|
|
|
|||
51
packages/renderer/src/draw/layout.ts
Normal file
51
packages/renderer/src/draw/layout.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import type { RendererDrawElementOptions, ViewContext2D, DataLayout, Element } from '@idraw/types';
|
||||
import { calcViewElementSize, calcViewBoxSize } from '@idraw/util';
|
||||
import { drawBoxShadow, drawBoxBackground, drawBoxBorder } from './box';
|
||||
|
||||
export function drawLayout(ctx: ViewContext2D, layout: DataLayout, opts: RendererDrawElementOptions, renderContent: (ctx: ViewContext2D) => void) {
|
||||
const { viewScaleInfo, viewSizeInfo, parentOpacity } = opts;
|
||||
const elem: Element = { uuid: 'layout', type: 'group', x: 0, y: 0, ...layout };
|
||||
const { x, y, w, h } = calcViewElementSize(elem, { viewScaleInfo, viewSizeInfo }) || elem;
|
||||
const angle = 0;
|
||||
const viewElem: Element = { ...elem, ...{ x, y, w, h, angle } } as Element;
|
||||
ctx.globalAlpha = 1;
|
||||
drawBoxShadow(ctx, viewElem, {
|
||||
viewScaleInfo,
|
||||
viewSizeInfo,
|
||||
renderContent: () => {
|
||||
drawBoxBackground(ctx, viewElem, { viewScaleInfo, viewSizeInfo });
|
||||
}
|
||||
});
|
||||
|
||||
if (layout.detail.overflow === 'hidden') {
|
||||
const { viewScaleInfo, viewSizeInfo } = opts;
|
||||
const elem: Element<'group'> = { uuid: 'layout', type: 'group', x: 0, y: 0, ...layout } as Element<'group'>;
|
||||
const viewElemSize = calcViewElementSize(elem, { viewScaleInfo, viewSizeInfo }) || elem;
|
||||
const viewElem = { ...elem, ...viewElemSize };
|
||||
const { x, y, w, h, radiusList } = calcViewBoxSize(viewElem, {
|
||||
viewScaleInfo,
|
||||
viewSizeInfo
|
||||
});
|
||||
ctx.save();
|
||||
|
||||
ctx.fillStyle = 'transparent';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + radiusList[0], y);
|
||||
ctx.arcTo(x + w, y, x + w, y + h, radiusList[1]);
|
||||
ctx.arcTo(x + w, y + h, x, y + h, radiusList[2]);
|
||||
ctx.arcTo(x, y + h, x, y, radiusList[3]);
|
||||
ctx.arcTo(x, y, x + w, y, radiusList[0]);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
ctx.clip();
|
||||
}
|
||||
|
||||
renderContent(ctx);
|
||||
|
||||
if (layout.detail.overflow === 'hidden') {
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
drawBoxBorder(ctx, viewElem, { viewScaleInfo, viewSizeInfo });
|
||||
ctx.globalAlpha = parentOpacity;
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
import type { RendererDrawElementOptions, ViewContext2D, DataUnderlay } from '@idraw/types';
|
||||
import { calcViewElementSize } from '@idraw/util';
|
||||
import { drawBox, drawBoxShadow } from './box';
|
||||
|
||||
export function drawUnderlay(ctx: ViewContext2D, underlay: DataUnderlay, opts: RendererDrawElementOptions) {
|
||||
const { viewScaleInfo, viewSizeInfo, parentOpacity } = opts;
|
||||
const elem = { uuid: 'underlay', ...underlay };
|
||||
const { x, y, w, h } = calcViewElementSize(elem, { viewScaleInfo, viewSizeInfo }) || elem;
|
||||
const angle = 0;
|
||||
const viewElem = { ...elem, ...{ x, y, w, h, angle } };
|
||||
drawBoxShadow(ctx, viewElem, {
|
||||
viewScaleInfo,
|
||||
viewSizeInfo,
|
||||
renderContent: () => {
|
||||
drawBox(ctx, viewElem, {
|
||||
originElem: elem,
|
||||
calcElemSize: { x, y, w, h, angle },
|
||||
viewScaleInfo,
|
||||
viewSizeInfo,
|
||||
parentOpacity,
|
||||
renderContent: () => {
|
||||
// empty
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { EventEmitter } from '@idraw/util';
|
||||
import type { LoadItemMap } from '@idraw/types';
|
||||
import { drawElementList, drawUnderlay } from './draw/index';
|
||||
import type { DataLayout, LoadItemMap } from '@idraw/types';
|
||||
import { drawElementList, drawLayout } from './draw/index';
|
||||
import { Loader } from './loader';
|
||||
import type { Data, BoardRenderer, RendererOptions, RendererEventMap, RendererDrawOptions } from '@idraw/types';
|
||||
|
||||
|
|
@ -54,23 +54,30 @@ export class Renderer extends EventEmitter<RendererEventMap> implements BoardRen
|
|||
w: opts.viewSizeInfo.width,
|
||||
h: opts.viewSizeInfo.height
|
||||
};
|
||||
if (data.underlay) {
|
||||
drawUnderlay(viewContext, data.underlay, {
|
||||
loader,
|
||||
calculator,
|
||||
parentElementSize,
|
||||
parentOpacity: 1,
|
||||
...opts
|
||||
});
|
||||
}
|
||||
drawElementList(viewContext, data, {
|
||||
// if (data.underlay) {
|
||||
// drawUnderlay(viewContext, data.underlay, {
|
||||
// loader,
|
||||
// calculator,
|
||||
// parentElementSize,
|
||||
// parentOpacity: 1,
|
||||
// ...opts
|
||||
// });
|
||||
// }
|
||||
const drawOpts = {
|
||||
loader,
|
||||
calculator,
|
||||
parentElementSize,
|
||||
elementAssets: data.assets,
|
||||
parentOpacity: 1,
|
||||
...opts
|
||||
});
|
||||
};
|
||||
if (data.layout) {
|
||||
drawLayout(viewContext, data.layout as DataLayout, drawOpts, () => {
|
||||
drawElementList(viewContext, data, drawOpts);
|
||||
});
|
||||
} else {
|
||||
drawElementList(viewContext, data, drawOpts);
|
||||
}
|
||||
}
|
||||
|
||||
scale(num: number) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import type { Element, ElementType, ElementAssets } from './element';
|
||||
|
||||
export type DataUnderlay = Omit<Element<'rect'>, 'uuid' | 'angle'>; // TODO color background
|
||||
import type { Element, ElementType, ElementAssets, ElementSize, ElementGroupDetail } from './element';
|
||||
|
||||
export type DataLayout = Pick<ElementSize, 'w' | 'h'> & {
|
||||
detail: Omit<ElementGroupDetail, 'children'>;
|
||||
};
|
||||
export interface Data<E extends Record<string, any> = Record<string, any>> {
|
||||
elements: Element<ElementType, E>[];
|
||||
assets?: ElementAssets;
|
||||
underlay?: DataUnderlay; // Rect or Color
|
||||
layout?: DataLayout;
|
||||
}
|
||||
|
||||
export type Matrix = [
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { CoreOptions } from './core';
|
|||
|
||||
export type IDrawMode = 'select' | 'drag' | 'readOnly';
|
||||
|
||||
export type IDrawFeature = 'ruler' | 'scroll' | 'scale'; // TODO other feature
|
||||
export type IDrawFeature = 'ruler' | 'scroll' | 'scale' | 'info'; // TODO other feature
|
||||
|
||||
export interface IDrawSettings {
|
||||
mode?: IDrawMode;
|
||||
|
|
@ -18,4 +18,5 @@ export interface IDrawStorage {
|
|||
enableSelect: boolean;
|
||||
enableTextEdit: boolean;
|
||||
enableDrag: boolean;
|
||||
enableInfo: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,8 +43,11 @@ export interface ViewCalculatorStorage {
|
|||
}
|
||||
|
||||
export interface ViewCalculator {
|
||||
isElementInView(elem: Element<ElementType>, viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): boolean;
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
isPointInElement(p: Point, elem: Element<ElementType>, viewScaleInfo: ViewScaleInfo, viewSize: ViewSizeInfo): boolean;
|
||||
needRender(elem: Element<ElementType>): boolean;
|
||||
getPointElement(
|
||||
p: Point,
|
||||
opts: { data: Data; viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo; groupQueue?: Element<'group'>[] }
|
||||
|
|
@ -82,7 +85,7 @@ export interface ViewCalculator {
|
|||
}
|
||||
): void;
|
||||
|
||||
toGridNum(num: number): number;
|
||||
toGridNum(num: number, opts?: { ignore?: boolean }): number;
|
||||
}
|
||||
|
||||
export type ViewRectVertexes = [PointSize, PointSize, PointSize, PointSize];
|
||||
|
|
|
|||
|
|
@ -119,6 +119,9 @@ export function calcViewVertexes(vertexes: ViewRectVertexes, opts: { viewScaleIn
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export function isViewPointInElement(
|
||||
p: Point,
|
||||
opts: { context2d: ViewContext2D; element: ElementSize; viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }
|
||||
|
|
@ -224,6 +227,9 @@ export function getViewPointAtElement(
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export function isElementInView(elem: ElementSize, opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): boolean {
|
||||
const { viewSizeInfo, viewScaleInfo } = opts;
|
||||
const { width, height } = viewSizeInfo;
|
||||
|
|
@ -434,7 +440,7 @@ export function calcElementViewRectInfoMap(
|
|||
|
||||
return {
|
||||
originRectInfo,
|
||||
viewRectInfo,
|
||||
// viewRectInfo,
|
||||
rangeRectInfo
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,39 +18,45 @@ export function calcViewCenterContent(data: Data, opts: { viewSizeInfo: ViewSize
|
|||
let contentY: number = data?.elements?.[0]?.y || 0;
|
||||
let contentW: number = data?.elements?.[0]?.w || 0;
|
||||
let contentH: number = data?.elements?.[0]?.h || 0;
|
||||
|
||||
const { width, height } = opts.viewSizeInfo;
|
||||
|
||||
data.elements.forEach((elem: Element) => {
|
||||
const elemSize: ElementSize = {
|
||||
x: elem.x,
|
||||
y: elem.y,
|
||||
w: elem.w,
|
||||
h: elem.h,
|
||||
angle: elem.angle
|
||||
};
|
||||
if (elemSize.angle && (elemSize.angle > 0 || elemSize.angle < 0)) {
|
||||
const ves = rotateElementVertexes(elemSize);
|
||||
if (ves.length === 4) {
|
||||
const xList = [ves[0].x, ves[1].x, ves[2].x, ves[3].x];
|
||||
const yList = [ves[0].y, ves[1].y, ves[2].y, ves[3].y];
|
||||
elemSize.x = Math.min(...xList);
|
||||
elemSize.y = Math.min(...yList);
|
||||
elemSize.w = Math.abs(Math.max(...xList) - Math.min(...xList));
|
||||
elemSize.h = Math.abs(Math.max(...yList) - Math.min(...yList));
|
||||
if (data.layout && data.layout?.detail?.overflow === 'hidden') {
|
||||
contentX = 0;
|
||||
contentY = 0;
|
||||
contentW = data.layout.w || 0;
|
||||
contentH = data.layout.h || 0;
|
||||
} else {
|
||||
data.elements.forEach((elem: Element) => {
|
||||
const elemSize: ElementSize = {
|
||||
x: elem.x,
|
||||
y: elem.y,
|
||||
w: elem.w,
|
||||
h: elem.h,
|
||||
angle: elem.angle
|
||||
};
|
||||
if (elemSize.angle && (elemSize.angle > 0 || elemSize.angle < 0)) {
|
||||
const ves = rotateElementVertexes(elemSize);
|
||||
if (ves.length === 4) {
|
||||
const xList = [ves[0].x, ves[1].x, ves[2].x, ves[3].x];
|
||||
const yList = [ves[0].y, ves[1].y, ves[2].y, ves[3].y];
|
||||
elemSize.x = Math.min(...xList);
|
||||
elemSize.y = Math.min(...yList);
|
||||
elemSize.w = Math.abs(Math.max(...xList) - Math.min(...xList));
|
||||
elemSize.h = Math.abs(Math.max(...yList) - Math.min(...yList));
|
||||
}
|
||||
}
|
||||
}
|
||||
const areaStartX = Math.min(elemSize.x, contentX);
|
||||
const areaStartY = Math.min(elemSize.y, contentY);
|
||||
const areaStartX = Math.min(elemSize.x, contentX);
|
||||
const areaStartY = Math.min(elemSize.y, contentY);
|
||||
|
||||
const areaEndX = Math.max(elemSize.x + elemSize.w, contentX + contentW);
|
||||
const areaEndY = Math.max(elemSize.y + elemSize.h, contentY + contentH);
|
||||
const areaEndX = Math.max(elemSize.x + elemSize.w, contentX + contentW);
|
||||
const areaEndY = Math.max(elemSize.y + elemSize.h, contentY + contentH);
|
||||
|
||||
contentX = areaStartX;
|
||||
contentY = areaStartY;
|
||||
contentW = Math.abs(areaEndX - areaStartX);
|
||||
contentH = Math.abs(areaEndY - areaStartY);
|
||||
});
|
||||
contentX = areaStartX;
|
||||
contentY = areaStartY;
|
||||
contentW = Math.abs(areaEndX - areaStartX);
|
||||
contentH = Math.abs(areaEndY - areaStartY);
|
||||
});
|
||||
}
|
||||
|
||||
if (contentW > 0 && contentH > 0) {
|
||||
const scaleW = formatNumber(width / contentW, { decimalPlaces: 4 });
|
||||
|
|
|
|||
Loading…
Reference in a new issue