diff --git a/packages/board/src/lib/watcher.ts b/packages/board/src/lib/watcher.ts index a5cb20d92..504049e07 100644 --- a/packages/board/src/lib/watcher.ts +++ b/packages/board/src/lib/watcher.ts @@ -38,7 +38,6 @@ export class BoardWatcher extends EventEmitter { container.removeEventListener('wheel', this.#onWheel); container.removeEventListener('click', this.#onClick); container.removeEventListener('contextmenu', this.#onContextMenu); - this.destroy(); } #onWheel = (e: WheelEvent) => { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4386ea0b3..e9711a556 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -14,19 +14,20 @@ export { MiddlewareDragger } from './middleware/dragger'; export class Core { #board: Board; // #opts: CoreOptions; - // #canvas: HTMLCanvasElement; + #canvas: HTMLCanvasElement; #container: HTMLDivElement; + constructor(container: HTMLDivElement, opts: CoreOptions) { - const { devicePixelRatio = 1, width, height } = opts; + const { devicePixelRatio = 1, width, height, createCustomContext2D } = opts; // this.#opts = opts; - // this.#canvas = canvas; this.#container = container; const canvas = document.createElement('canvas'); + this.#canvas = canvas; this.#initContainer(); container.appendChild(canvas); - const boardContent = createBoardContent(canvas, { width, height, devicePixelRatio, offscreen: true }); + const boardContent = createBoardContent(canvas, { width, height, devicePixelRatio, offscreen: true, createCustomContext2D }); const board = new Board({ boardContent, container }); const sharer = board.getSharer(); sharer.setActiveViewSizeInfo({ @@ -46,6 +47,7 @@ export class Core { destroy() { this.#board.destroy(); + this.#canvas.remove(); } #initContainer() { diff --git a/packages/core/src/middleware/selector/config.ts b/packages/core/src/middleware/selector/config.ts index e1b6a66b4..4dd30c902 100644 --- a/packages/core/src/middleware/selector/config.ts +++ b/packages/core/src/middleware/selector/config.ts @@ -24,4 +24,6 @@ export const selectWrapperBorderWidth = 2; export const resizeControllerBorderWidth = 4; export const areaBorderWidth = 1; export const wrapperColor = '#1973ba'; + +export const lockColor = '#5b5959b5'; // export const wrapperColor = '#1890ff'; diff --git a/packages/core/src/middleware/selector/draw-wrapper.ts b/packages/core/src/middleware/selector/draw-wrapper.ts index 359769b0d..8176264ae 100644 --- a/packages/core/src/middleware/selector/draw-wrapper.ts +++ b/packages/core/src/middleware/selector/draw-wrapper.ts @@ -12,7 +12,7 @@ import type { import { rotateElementVertexes, calcViewVertexes } from '@idraw/util'; import type { AreaSize } from './types'; -import { resizeControllerBorderWidth, areaBorderWidth, wrapperColor, selectWrapperBorderWidth } from './config'; +import { resizeControllerBorderWidth, areaBorderWidth, wrapperColor, selectWrapperBorderWidth, lockColor } from './config'; function drawVertexes( ctx: ViewContext2D, @@ -51,6 +51,61 @@ export function drawHoverVertexesWrapper( drawVertexes(ctx, calcViewVertexes(vertexes, opts), wrapperOpts); } +function drawCrossVertexes( + ctx: ViewContext2D, + vertexes: ViewRectVertexes, + opts: { borderColor: string; borderWidth: number; background: string; lineDash: number[] } +) { + const { borderColor, borderWidth, background, lineDash } = opts; + ctx.setLineDash([]); + ctx.lineWidth = borderWidth; + ctx.strokeStyle = borderColor; + ctx.fillStyle = background; + ctx.setLineDash(lineDash); + ctx.beginPath(); + ctx.moveTo(vertexes[0].x, vertexes[0].y); + ctx.lineTo(vertexes[2].x, vertexes[2].y); + ctx.closePath(); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(vertexes[1].x, vertexes[1].y); + ctx.lineTo(vertexes[3].x, vertexes[3].y); + ctx.closePath(); + ctx.stroke(); +} + +export function drawLockVertexesWrapper( + ctx: ViewContext2D, + vertexes: ViewRectVertexes | null, + opts: { + viewScaleInfo: ViewScaleInfo; + viewSizeInfo: ViewSizeInfo; + controller?: ElementSizeController | null; + } +) { + if (!vertexes) { + return; + } + const wrapperOpts = { borderColor: lockColor, borderWidth: 1, background: 'transparent', lineDash: [] }; + drawVertexes(ctx, calcViewVertexes(vertexes, opts), wrapperOpts); + + const { controller } = opts; + if (controller) { + const { topLeft, topRight, bottomLeft, bottomRight, topMiddle, bottomMiddle, leftMiddle, rightMiddle } = controller; + const ctrlOpts = { ...wrapperOpts, borderWidth: 1, background: lockColor }; + + drawCrossVertexes(ctx, calcViewVertexes(topMiddle.vertexes, opts), ctrlOpts); + drawCrossVertexes(ctx, calcViewVertexes(bottomMiddle.vertexes, opts), ctrlOpts); + drawCrossVertexes(ctx, calcViewVertexes(leftMiddle.vertexes, opts), ctrlOpts); + drawCrossVertexes(ctx, calcViewVertexes(rightMiddle.vertexes, opts), ctrlOpts); + + drawCrossVertexes(ctx, calcViewVertexes(topLeft.vertexes, opts), ctrlOpts); + drawCrossVertexes(ctx, calcViewVertexes(topRight.vertexes, opts), ctrlOpts); + drawCrossVertexes(ctx, calcViewVertexes(bottomLeft.vertexes, opts), ctrlOpts); + drawCrossVertexes(ctx, calcViewVertexes(bottomRight.vertexes, opts), ctrlOpts); + } +} + export function drawSelectedElementControllersVertexes( ctx: ViewContext2D, controller: ElementSizeController | null, @@ -60,7 +115,6 @@ export function drawSelectedElementControllersVertexes( return; } const { elementWrapper, topLeft, topRight, bottomLeft, bottomRight } = controller; - // const wrapperColor = 'red'; // TODO const wrapperOpts = { borderColor: wrapperColor, borderWidth: selectWrapperBorderWidth, background: 'transparent', lineDash: [] }; const ctrlOpts = { ...wrapperOpts, borderWidth: resizeControllerBorderWidth, background: '#FFFFFF' }; diff --git a/packages/core/src/middleware/selector/index.ts b/packages/core/src/middleware/selector/index.ts index 29788c5be..2a377037a 100644 --- a/packages/core/src/middleware/selector/index.ts +++ b/packages/core/src/middleware/selector/index.ts @@ -16,14 +16,20 @@ import type { PointWatcherEvent, BoardMiddleware, Element, - ElementSize, ActionType, ResizeType, DeepSelectorSharedStorage, ElementType, PointTarget } from './types'; -import { drawHoverVertexesWrapper, drawArea, drawListArea, drawGroupQueueVertexesWrappers, drawSelectedElementControllersVertexes } from './draw-wrapper'; +import { + drawHoverVertexesWrapper, + drawLockVertexesWrapper, + drawArea, + drawListArea, + drawGroupQueueVertexesWrappers, + drawSelectedElementControllersVertexes +} from './draw-wrapper'; import { getPointTarget, resizeElement, @@ -198,7 +204,6 @@ export const MiddlewareSelector: BoardMiddleware { const cursor: string | null = target.type; if (inBusyMode === null) { @@ -288,7 +293,7 @@ export const MiddlewareSelector: BoardMiddleware { prevPoint = e.point; - updateHoverElement(null); + // updateHoverElement(null); const groupQueue = sharer.getSharedStorage(keyGroupQueue); if (groupQueue?.length > 0) { @@ -317,7 +322,12 @@ export const MiddlewareSelector: BoardMiddleware) => { - if (elem) { + if (elem && elem?.operations?.lock !== true) { elem.x += moveX; elem.y += moveY; } @@ -540,6 +555,12 @@ export const MiddlewareSelector: BoardMiddleware); @@ -567,7 +588,7 @@ export const MiddlewareSelector: BoardMiddleware 0) { // in group drawGroupQueueVertexesWrappers(helperContext, groupQueueVertexesList, drawBaseOpts); if (hoverElement && actionType !== 'drag') { - drawHoverVertexesWrapper(helperContext, hoverElementVertexes, drawBaseOpts); + if (isLock) { + drawLockVertexesWrapper(helperContext, hoverElementVertexes, { + ...drawBaseOpts, + controller: calcElementSizeController(hoverElement, { + groupQueue, + controllerSize: 10, + viewScaleInfo + }) + }); + } else { + drawHoverVertexesWrapper(helperContext, hoverElementVertexes, drawBaseOpts); + } } - if (elem && (['select', 'drag', 'resize'] as ActionType[]).includes(actionType)) { + if (!isLock && elem && (['select', 'drag', 'resize'] as ActionType[]).includes(actionType)) { drawSelectedElementControllersVertexes(helperContext, selectedElementController, { ...drawBaseOpts }); } } else { // in root if (hoverElement && actionType !== 'drag') { - drawHoverVertexesWrapper(helperContext, hoverElementVertexes, drawBaseOpts); + if (isLock) { + drawLockVertexesWrapper(helperContext, hoverElementVertexes, { + ...drawBaseOpts, + controller: calcElementSizeController(hoverElement, { + groupQueue, + controllerSize: 10, + viewScaleInfo + }) + }); + } else { + drawHoverVertexesWrapper(helperContext, hoverElementVertexes, drawBaseOpts); + } } - if (elem && (['select', 'drag', 'resize'] as ActionType[]).includes(actionType)) { + if (!isLock && elem && (['select', 'drag', 'resize'] as ActionType[]).includes(actionType)) { drawSelectedElementControllersVertexes(helperContext, selectedElementController, { ...drawBaseOpts }); } else if (actionType === 'area' && areaStart && areaEnd) { drawArea(helperContext, { start: areaStart, end: areaEnd }); diff --git a/packages/core/src/middleware/selector/util.ts b/packages/core/src/middleware/selector/util.ts index 49d112ce9..82d2b75ca 100644 --- a/packages/core/src/middleware/selector/util.ts +++ b/packages/core/src/middleware/selector/util.ts @@ -842,7 +842,11 @@ export function getSelectedListArea( const startY = Math.min(start.y, end.y); const endY = Math.max(start.y, end.y); - data.elements.forEach((elem, idx) => { + for (let idx = 0; idx < data.elements.length; idx++) { + const elem = data.elements[idx]; + if (elem?.operations?.lock === true) { + continue; + } const elemSize = calculator.elementSize(elem, viewScaleInfo, viewSizeInfo); const center = calcElementCenter(elemSize); @@ -862,7 +866,8 @@ export function getSelectedListArea( } } } - }); + } + return { indexes, uuids, elements }; } diff --git a/packages/idraw/src/idraw.ts b/packages/idraw/src/idraw.ts index 567cc8767..ff9df7235 100644 --- a/packages/idraw/src/idraw.ts +++ b/packages/idraw/src/idraw.ts @@ -29,7 +29,8 @@ import { moveElementPosition, getElementPositionFromList, calcElementListSize, - filterCompactData + filterCompactData, + calcViewCenterContent } from '@idraw/util'; import { defaultSettings } from './config'; import { exportImageFileBlobURL } from './file'; @@ -41,8 +42,8 @@ export class iDraw { constructor(mount: HTMLDivElement, options: IDrawOptions) { const opts = { ...defaultSettings, ...options }; - const { width, height, devicePixelRatio } = opts; - const core = new Core(mount, { width, height, devicePixelRatio }); + const { width, height, devicePixelRatio, createCustomContext2D } = opts; + const core = new Core(mount, { width, height, devicePixelRatio, createCustomContext2D }); this.#core = core; this.#opts = opts; this.#init(); @@ -139,6 +140,15 @@ export class iDraw { core.refresh(); } + centerContent(opts?: { data?: Data }) { + const data = opts?.data || this.#core.getData(); + const { viewSizeInfo } = this.getViewInfo(); + if (data) { + const result = calcViewCenterContent(data, { viewSizeInfo }); + this.setViewScale(result); + } + } + resize(opts: Partial) { this.#core.resize(opts); } @@ -263,4 +273,9 @@ export class iDraw { loadItemMap: this.#core.getLoadItemMap() }); } + + destroy() { + const core = this.#core; + core.destroy(); + } } diff --git a/packages/idraw/src/index.ts b/packages/idraw/src/index.ts index 48c6d84dd..8fc33b62f 100644 --- a/packages/idraw/src/index.ts +++ b/packages/idraw/src/index.ts @@ -115,7 +115,8 @@ export { deleteElementInListByPosition, deleteElementInList, deepResizeGroupElement, - deepCloneElement + deepCloneElement, + calcViewCenterContent } from '@idraw/util'; export { iDraw } from './idraw'; export type { IDrawEvent, IDrawEventKeys } from './event'; diff --git a/packages/renderer/src/loader.ts b/packages/renderer/src/loader.ts index f37b02ed6..efb1707bc 100644 --- a/packages/renderer/src/loader.ts +++ b/packages/renderer/src/loader.ts @@ -112,7 +112,7 @@ export class Loader extends EventEmitter implements RendererLoad #emitError(item: LoadItem) { const assetId = getAssetIdFromElement(item.element); - const storageItem = this.#storageLoadItemMap[assetId]; + const storageItem = this.#storageLoadItemMap?.[assetId]; if (storageItem) { if (storageItem.startTime < item.startTime) { this.#storageLoadItemMap[assetId] = item; diff --git a/packages/types/src/lib/context2d.ts b/packages/types/src/lib/context2d.ts index 53b01fc98..6a8806b50 100644 --- a/packages/types/src/lib/context2d.ts +++ b/packages/types/src/lib/context2d.ts @@ -8,6 +8,7 @@ export interface ViewContext2D { // extend API $getContext(): CanvasRenderingContext2D; + $setContext(ctx: CanvasRenderingContext2D): void; $setFont(opts: { fontSize: number; fontFamily?: string; fontWeight?: string | number }): void; $resize(opts: { width: number; height: number; devicePixelRatio: number }): void; $getSize(): { width: number; height: number; devicePixelRatio: number }; diff --git a/packages/types/src/lib/controller.ts b/packages/types/src/lib/controller.ts index acab6c404..438b5253d 100644 --- a/packages/types/src/lib/controller.ts +++ b/packages/types/src/lib/controller.ts @@ -1,7 +1,19 @@ import { ViewRectVertexes } from './view'; import { PointSize } from './point'; -export type ElementSizeControllerType = 'left' | 'right' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'; +export type ElementSizeControllerType = + | 'left' + | 'right' + | 'top' + | 'bottom' + | 'top-left' + | 'top-right' + | 'bottom-left' + | 'bottom-right' + | 'left-middle' + | 'right-middle' + | 'top-middle' + | 'bottom-middle'; export interface ElementSizeControllerItem { type: ElementSizeControllerType; @@ -19,4 +31,9 @@ export interface ElementSizeController { topRight: ElementSizeControllerItem; bottomLeft: ElementSizeControllerItem; bottomRight: ElementSizeControllerItem; + + topMiddle: ElementSizeControllerItem; + bottomMiddle: ElementSizeControllerItem; + leftMiddle: ElementSizeControllerItem; + rightMiddle: ElementSizeControllerItem; } diff --git a/packages/types/src/lib/core.ts b/packages/types/src/lib/core.ts index 532d92bfc..2a8e67c2f 100644 --- a/packages/types/src/lib/core.ts +++ b/packages/types/src/lib/core.ts @@ -1,10 +1,12 @@ import type { Element, ElementType } from './element'; import type { Data } from './data'; +import type { ViewContext2D } from './context2d'; export interface CoreOptions { width: number; height: number; devicePixelRatio?: number; + createCustomContext2D?: (opts: { width: number; height: number; devicePixelRatio: number }) => ViewContext2D; } export type CursorType = diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts index 62a9e0fc8..568f63a30 100644 --- a/packages/util/src/index.ts +++ b/packages/util/src/index.ts @@ -74,3 +74,4 @@ export { updateElementInList } from './lib/handle-element'; export { deepResizeGroupElement } from './lib/resize-element'; +export { calcViewCenterContent } from './lib/view-content'; diff --git a/packages/util/src/lib/canvas.ts b/packages/util/src/lib/canvas.ts index 06baa70d4..87ceaefca 100644 --- a/packages/util/src/lib/canvas.ts +++ b/packages/util/src/lib/canvas.ts @@ -1,4 +1,4 @@ -import type { BoardContent } from '@idraw/types'; +import type { BoardContent, ViewContext2D } from '@idraw/types'; import { Context2D } from './context2d'; export function createContext2D(opts: { ctx?: CanvasRenderingContext2D; width: number; height: number; devicePixelRatio: number }): Context2D { @@ -30,17 +30,54 @@ export function createOffscreenContext2D(opts: { width: number; height: number; export function createBoardContent( canvas: HTMLCanvasElement, - opts: { width: number; height: number; devicePixelRatio: number; offscreen?: boolean } + opts: { + width: number; + height: number; + devicePixelRatio: number; + offscreen?: boolean; + createCustomContext2D?: (opts: { width: number; height: number; devicePixelRatio: number }) => ViewContext2D; + } ): BoardContent { - const { width, height, devicePixelRatio, offscreen } = opts; + const { width, height, devicePixelRatio, offscreen, createCustomContext2D } = opts; const ctxOpts = { width, height, devicePixelRatio }; + const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; + + if (createCustomContext2D) { + // TODO + const viewContext = createCustomContext2D(ctxOpts); + const helperContext = createCustomContext2D(ctxOpts); + const underContext = createCustomContext2D(ctxOpts); + const boardContext = createContext2D({ ctx, ...ctxOpts }); + + const drawView = () => { + const { width: w, height: h } = viewContext.$getSize(); + + boardContext.clearRect(0, 0, w, h); + boardContext.drawImage(underContext.canvas, 0, 0, w, h); + boardContext.drawImage(viewContext.canvas, 0, 0, w, h); + boardContext.drawImage(helperContext.canvas, 0, 0, w, h); + underContext.clearRect(0, 0, w, h); + viewContext.clearRect(0, 0, w, h); + helperContext.clearRect(0, 0, w, h); + }; + + const content: BoardContent = { + underContext, + viewContext, + helperContext, + boardContext, + drawView + }; + return content; + } + if (offscreen === true) { - const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; + // const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; const viewContext = createOffscreenContext2D(ctxOpts); const helperContext = createOffscreenContext2D(ctxOpts); const underContext = createOffscreenContext2D(ctxOpts); @@ -67,7 +104,7 @@ export function createBoardContent( }; return content; } else { - const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; + // const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; const viewContext = createContext2D(ctxOpts); const helperContext = createContext2D(ctxOpts); const underContext = createContext2D(ctxOpts); diff --git a/packages/util/src/lib/context2d.ts b/packages/util/src/lib/context2d.ts index 9c0b2c2e1..51f8c69ba 100644 --- a/packages/util/src/lib/context2d.ts +++ b/packages/util/src/lib/context2d.ts @@ -25,6 +25,10 @@ export class Context2D implements ViewContext2D { return this.#ctx; } + $setContext(ctx: CanvasRenderingContext2D) { + this.#ctx = ctx; + } + $setFont(opts: { fontSize: number; fontFamily?: string; fontWeight?: 'bold' | number | string }): void { const strList: string[] = []; if (opts.fontWeight) { diff --git a/packages/util/src/lib/controller.ts b/packages/util/src/lib/controller.ts index 617612ca8..3fa473868 100644 --- a/packages/util/src/lib/controller.ts +++ b/packages/util/src/lib/controller.ts @@ -58,10 +58,10 @@ export function calcElementSizeController( const bottomRightCenter = vertexes[2]; const bottomLeftCenter = vertexes[3]; - // const topSize = createControllerElementSizeFromCenter(topCenter, { size: ctrlSize, angle: totalAngle }); - // const rightSize = createControllerElementSizeFromCenter(rightCenter, { size: ctrlSize, angle: totalAngle }); - // const bottomSize = createControllerElementSizeFromCenter(bottomCenter, { size: ctrlSize, angle: totalAngle }); - // const leftSize = createControllerElementSizeFromCenter(leftCenter, { size: ctrlSize, angle: totalAngle }); + const topMiddleSize = createControllerElementSizeFromCenter(topCenter, { size: ctrlSize, angle: totalAngle }); + const rightMiddleSize = createControllerElementSizeFromCenter(rightCenter, { size: ctrlSize, angle: totalAngle }); + const bottomMiddleSize = createControllerElementSizeFromCenter(bottomCenter, { size: ctrlSize, angle: totalAngle }); + const leftMiddleSize = createControllerElementSizeFromCenter(leftCenter, { size: ctrlSize, angle: totalAngle }); const topLeftSize = createControllerElementSizeFromCenter(topLeftCenter, { size: ctrlSize, angle: totalAngle }); const topRightSize = createControllerElementSizeFromCenter(topRightCenter, { size: ctrlSize, angle: totalAngle }); @@ -77,10 +77,11 @@ export function calcElementSizeController( const rightVertexes: ViewRectVertexes = [topRightVertexes[3], topRightVertexes[2], bottomRightVertexes[1], bottomRightVertexes[0]]; const bottomVertexes: ViewRectVertexes = [bottomLeftVertexes[1], bottomRightVertexes[0], bottomRightVertexes[3], bottomLeftVertexes[2]]; const leftVertexes: ViewRectVertexes = [topLeftVertexes[3], topLeftVertexes[2], bottomLeftVertexes[1], bottomLeftVertexes[0]]; - // const topVertexes = calcElementVertexes(topSize); - // const rightVertexes = calcElementVertexes(rightSize); - // const bottomVertexes = calcElementVertexes(bottomSize); - // const leftVertexes = calcElementVertexes(leftSize); + + const topMiddleVertexes = calcElementVertexes(topMiddleSize); + const rightMiddleVertexes = calcElementVertexes(rightMiddleSize); + const bottomMiddleVertexes = calcElementVertexes(bottomMiddleSize); + const leftMiddleVertexes = calcElementVertexes(leftMiddleSize); const sizeController: ElementSizeController = { elementWrapper: vertexes, @@ -123,6 +124,26 @@ export function calcElementSizeController( type: 'bottom-right', vertexes: bottomRightVertexes, center: bottomRightCenter + }, + leftMiddle: { + type: 'left-middle', + vertexes: leftMiddleVertexes, + center: leftCenter + }, + rightMiddle: { + type: 'right-middle', + vertexes: rightMiddleVertexes, + center: rightCenter + }, + topMiddle: { + type: 'top-middle', + vertexes: topMiddleVertexes, + center: topCenter + }, + bottomMiddle: { + type: 'bottom-middle', + vertexes: bottomMiddleVertexes, + center: bottomCenter } }; return sizeController; diff --git a/packages/util/src/lib/view-content.ts b/packages/util/src/lib/view-content.ts new file mode 100644 index 000000000..0ab4ae975 --- /dev/null +++ b/packages/util/src/lib/view-content.ts @@ -0,0 +1,69 @@ +import type { Data, ViewSizeInfo, Element, ElementSize } from 'idraw'; +import { rotateElementVertexes } from './rotate'; +import {} from './view-calc'; +import { formatNumber } from './number'; + +interface ViewCenterContentResult { + offsetX: number; + offsetY: number; + scale: number; +} + +export function calcViewCenterContent(data: Data, opts: { viewSizeInfo: ViewSizeInfo }): ViewCenterContentResult { + let offsetX: number = 0; + let offsetY: number = 0; + let scale: number = 0; + + let contentX: number = 0; + let contentY: number = 0; + let contentW: number = 0; + let contentH: number = 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)); + } + } + 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); + + 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 }); + const scaleH = formatNumber(height / contentH, { decimalPlaces: 4 }); + scale = Math.min(scaleW, scaleH, 1); + offsetX = (contentW * scale - width) / 2 / scale; + offsetY = (contentH * scale - height) / 2 / scale; + } + + const result: ViewCenterContentResult = { + offsetX: formatNumber(offsetX, { decimalPlaces: 0 }), + offsetY: formatNumber(offsetY, { decimalPlaces: 0 }), + scale + }; + return result; +}