mirror of
https://github.com/idrawjs/idraw
synced 2026-05-24 10:08:34 +00:00
feat: improve text-editor, fix getData and fix type error
This commit is contained in:
parent
a3ed9dd744
commit
bf3629a977
22 changed files with 438 additions and 94 deletions
|
|
@ -34,7 +34,7 @@ export class Board<T extends BoardExtendEvent = BoardExtendEvent> {
|
|||
#viewer: Viewer;
|
||||
#calculator: Calculator;
|
||||
#eventHub: EventEmitter<T> = new EventEmitter<T>();
|
||||
|
||||
#hasDestroyed: boolean = false;
|
||||
constructor(opts: BoardOptions) {
|
||||
const { boardContent } = opts;
|
||||
const sharer = new Sharer();
|
||||
|
|
@ -70,6 +70,10 @@ export class Board<T extends BoardExtendEvent = BoardExtendEvent> {
|
|||
this.#resetActiveMiddlewareObjs();
|
||||
}
|
||||
|
||||
isDestroyed() {
|
||||
return this.#hasDestroyed;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
// #opts
|
||||
// #middlewareMap
|
||||
|
|
@ -81,6 +85,7 @@ export class Board<T extends BoardExtendEvent = BoardExtendEvent> {
|
|||
// #viewer: Viewer;
|
||||
this.#calculator.destroy();
|
||||
this.#eventHub.destroy();
|
||||
this.#hasDestroyed = true;
|
||||
}
|
||||
|
||||
#init() {
|
||||
|
|
@ -376,6 +381,14 @@ export class Board<T extends BoardExtendEvent = BoardExtendEvent> {
|
|||
getEventHub(): EventEmitter<T> {
|
||||
return this.#eventHub;
|
||||
}
|
||||
|
||||
onWatcherEvents() {
|
||||
this.#watcher.onEvents();
|
||||
}
|
||||
|
||||
offWatcherEvents() {
|
||||
this.#watcher.offEvents();
|
||||
}
|
||||
}
|
||||
|
||||
export { Sharer, Calculator };
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ function isBoardAvailableNum(num: any): boolean {
|
|||
export class BoardWatcher extends EventEmitter<BoardWatcherEventMap> {
|
||||
#opts: BoardWatcherOptions;
|
||||
#store: Store<BoardWatcherStore>;
|
||||
#hasDestroyed: boolean = false;
|
||||
constructor(opts: BoardWatcherOptions) {
|
||||
super();
|
||||
const store = new Store<BoardWatcherStore>({ defaultStorage: { hasPointDown: false, prevClickPoint: null } });
|
||||
|
|
@ -17,6 +18,13 @@ export class BoardWatcher extends EventEmitter<BoardWatcherEventMap> {
|
|||
}
|
||||
|
||||
#init() {
|
||||
this.onEvents();
|
||||
}
|
||||
|
||||
onEvents() {
|
||||
if (this.#hasDestroyed) {
|
||||
return;
|
||||
}
|
||||
const container = window;
|
||||
container.addEventListener('mousemove', this.#onHover);
|
||||
container.addEventListener('mousedown', this.#onPointStart);
|
||||
|
|
@ -28,7 +36,7 @@ export class BoardWatcher extends EventEmitter<BoardWatcherEventMap> {
|
|||
container.addEventListener('contextmenu', this.#onContextMenu);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
offEvents() {
|
||||
const container = window;
|
||||
container.removeEventListener('mousemove', this.#onHover);
|
||||
container.removeEventListener('mousedown', this.#onPointStart);
|
||||
|
|
@ -40,6 +48,12 @@ export class BoardWatcher extends EventEmitter<BoardWatcherEventMap> {
|
|||
container.removeEventListener('contextmenu', this.#onContextMenu);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.offEvents();
|
||||
this.#store.destroy();
|
||||
this.#hasDestroyed = true;
|
||||
}
|
||||
|
||||
#onWheel = (e: WheelEvent) => {
|
||||
if (!this.#isInTarget(e)) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -45,6 +45,10 @@ export class Core<E extends CoreEvent = CoreEvent> {
|
|||
});
|
||||
}
|
||||
|
||||
isDestroyed() {
|
||||
return this.#board.isDestroyed();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.#board.destroy();
|
||||
this.#canvas.remove();
|
||||
|
|
@ -129,4 +133,12 @@ export class Core<E extends CoreEvent = CoreEvent> {
|
|||
getLoadItemMap(): LoadItemMap {
|
||||
return this.#board.getRenderer().getLoadItemMap();
|
||||
}
|
||||
|
||||
onBoardWatcherEvents() {
|
||||
this.#board.onWatcherEvents();
|
||||
}
|
||||
|
||||
offBoardWatcherEvents() {
|
||||
this.#board.offWatcherEvents();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import { limitAngle, loadImage, parseAngleToRadian } from '@idraw/util';
|
|||
import { CURSOR, CURSOR_RESIZE, CURSOR_DRAG_DEFAULT, CURSOR_DRAG_ACTIVE } from './cursor-image';
|
||||
|
||||
export class Cursor {
|
||||
private _eventHub: UtilEventEmitter<CoreEvent>;
|
||||
private _container: HTMLDivElement;
|
||||
private _cursorType: 'auto' | string | null = null;
|
||||
private _resizeCursorBaseImage: HTMLImageElement | null = null;
|
||||
private _cursorImageMap: Record<string, string> = {
|
||||
#eventHub: UtilEventEmitter<CoreEvent>;
|
||||
#container: HTMLDivElement;
|
||||
#cursorType: 'default' | string | null = null;
|
||||
#resizeCursorBaseImage: HTMLImageElement | null = null;
|
||||
#cursorImageMap: Record<string, string> = {
|
||||
auto: CURSOR,
|
||||
'drag-default': CURSOR_DRAG_DEFAULT,
|
||||
'drag-active': CURSOR_DRAG_ACTIVE,
|
||||
|
|
@ -19,56 +19,60 @@ export class Cursor {
|
|||
eventHub: UtilEventEmitter<CoreEvent>;
|
||||
}
|
||||
) {
|
||||
this._container = container;
|
||||
this._eventHub = opts.eventHub;
|
||||
this._init();
|
||||
this._loadResizeCursorBaseImage();
|
||||
this.#container = container;
|
||||
this.#eventHub = opts.eventHub;
|
||||
this.#init();
|
||||
this.#loadResizeCursorBaseImage();
|
||||
}
|
||||
|
||||
private _init() {
|
||||
const { _eventHub: eventHub } = this;
|
||||
this._resetCursor('auto');
|
||||
#init() {
|
||||
const eventHub = this.#eventHub;
|
||||
this.#resetCursor('default');
|
||||
eventHub.on('cursor', (e) => {
|
||||
if (e.type === 'over-element' || !e.type) {
|
||||
this._resetCursor('auto');
|
||||
this.#resetCursor('auto');
|
||||
} else if (typeof e.type === 'string' && e.type?.startsWith('resize-')) {
|
||||
this._setCursorResize(e);
|
||||
this.#setCursorResize(e);
|
||||
} else if (e.type === 'drag-default') {
|
||||
this._resetCursor('drag-default');
|
||||
this.#resetCursor('drag-default');
|
||||
} else if (e.type === 'drag-active') {
|
||||
this._resetCursor('drag-active');
|
||||
this.#resetCursor('drag-active');
|
||||
} else {
|
||||
this._resetCursor('auto');
|
||||
this.#resetCursor('auto');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _loadResizeCursorBaseImage() {
|
||||
#loadResizeCursorBaseImage() {
|
||||
loadImage(CURSOR_RESIZE)
|
||||
.then((img) => {
|
||||
this._resizeCursorBaseImage = img;
|
||||
this.#resizeCursorBaseImage = img;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
private _resetCursor(cursorKey: string) {
|
||||
if (this._cursorType === cursorKey) {
|
||||
#resetCursor(cursorKey: string) {
|
||||
if (this.#cursorType === cursorKey) {
|
||||
return;
|
||||
}
|
||||
this._cursorType = cursorKey;
|
||||
const image = this._cursorImageMap[this._cursorType] || this._cursorImageMap['auto'];
|
||||
this.#cursorType = cursorKey;
|
||||
const image = this.#cursorImageMap[this.#cursorType] || this.#cursorImageMap['auto'];
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
if (cursorKey.startsWith('rotate-') && this._cursorImageMap[this._cursorType]) {
|
||||
if (cursorKey.startsWith('rotate-') && this.#cursorImageMap[this.#cursorType]) {
|
||||
offsetX = 10;
|
||||
offsetY = 10;
|
||||
}
|
||||
this._container.style.cursor = `image-set(url(${image})2x) ${offsetX} ${offsetY}, auto`;
|
||||
if (cursorKey === 'default') {
|
||||
this.#container.style.cursor = 'default';
|
||||
} else {
|
||||
this.#container.style.cursor = `image-set(url(${image})2x) ${offsetX} ${offsetY}, auto`;
|
||||
}
|
||||
}
|
||||
|
||||
private _setCursorResize(e: CoreEvent['cursor']) {
|
||||
#setCursorResize(e: CoreEvent['cursor']) {
|
||||
let totalAngle = 0;
|
||||
if (e.type === 'resize-top') {
|
||||
totalAngle += 0;
|
||||
|
|
@ -94,14 +98,14 @@ export class Cursor {
|
|||
});
|
||||
}
|
||||
totalAngle = limitAngle(totalAngle);
|
||||
const cursorKey = this._appendRotateResizeImage(totalAngle);
|
||||
this._resetCursor(cursorKey);
|
||||
const cursorKey = this.#appendRotateResizeImage(totalAngle);
|
||||
this.#resetCursor(cursorKey);
|
||||
}
|
||||
|
||||
private _appendRotateResizeImage(angle: number): string {
|
||||
#appendRotateResizeImage(angle: number): string {
|
||||
const key = `rotate-${angle}`;
|
||||
if (!this._cursorImageMap[key]) {
|
||||
const baseImage = this._resizeCursorBaseImage;
|
||||
if (!this.#cursorImageMap[key]) {
|
||||
const baseImage = this.#resizeCursorBaseImage;
|
||||
if (baseImage) {
|
||||
const canvas = document.createElement('canvas');
|
||||
const w = baseImage.width;
|
||||
|
|
@ -126,7 +130,7 @@ export class Cursor {
|
|||
ctx.translate(-center.x, -center.y);
|
||||
|
||||
const base = canvas.toDataURL('image/png');
|
||||
this._cursorImageMap[key] = base;
|
||||
this.#cursorImageMap[key] = base;
|
||||
}
|
||||
}
|
||||
return key;
|
||||
|
|
|
|||
|
|
@ -110,10 +110,10 @@ export const MiddlewareTextEditor: BoardMiddleware<Record<string, any>, CoreEven
|
|||
}
|
||||
|
||||
textarea.style.position = 'absolute';
|
||||
textarea.style.left = `${elemX}px`;
|
||||
textarea.style.top = `${elemY}px`;
|
||||
textarea.style.width = `${elemW}px`;
|
||||
textarea.style.height = `${elemH}px`;
|
||||
textarea.style.left = `${elemX - 1}px`;
|
||||
textarea.style.top = `${elemY - 1}px`;
|
||||
textarea.style.width = `${elemW + 2}px`;
|
||||
textarea.style.height = `${elemH + 2}px`;
|
||||
textarea.style.transform = `rotate(${limitAngle(element.angle || 0)}deg)`;
|
||||
// textarea.style.border = 'none';
|
||||
textarea.style.boxSizing = 'border-box';
|
||||
|
|
@ -127,6 +127,10 @@ export const MiddlewareTextEditor: BoardMiddleware<Record<string, any>, CoreEven
|
|||
textarea.style.lineHeight = `${detail.lineHeight * scale}px`;
|
||||
textarea.style.fontFamily = detail.fontFamily;
|
||||
textarea.style.fontWeight = `${detail.fontWeight}`;
|
||||
textarea.style.padding = '0';
|
||||
textarea.style.margin = '0';
|
||||
textarea.style.outline = 'none';
|
||||
|
||||
textarea.value = detail.text || '';
|
||||
parent.appendChild(textarea);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ import {
|
|||
getElementPositionFromList,
|
||||
calcElementListSize,
|
||||
filterCompactData,
|
||||
calcViewCenterContent
|
||||
calcViewCenterContent,
|
||||
calcViewCenter
|
||||
} from '@idraw/util';
|
||||
import { defaultSettings } from './config';
|
||||
import { exportImageFileBlobURL } from './file';
|
||||
|
|
@ -143,7 +144,7 @@ export class iDraw {
|
|||
centerContent(opts?: { data?: Data }) {
|
||||
const data = opts?.data || this.#core.getData();
|
||||
const { viewSizeInfo } = this.getViewInfo();
|
||||
if (data) {
|
||||
if (Array.isArray(data?.elements) && data?.elements.length > 0) {
|
||||
const result = calcViewCenterContent(data, { viewSizeInfo });
|
||||
this.setViewScale(result);
|
||||
}
|
||||
|
|
@ -274,8 +275,25 @@ export class iDraw {
|
|||
});
|
||||
}
|
||||
|
||||
isDestroyed() {
|
||||
return this.#core.isDestroyed();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
const core = this.#core;
|
||||
core.destroy();
|
||||
}
|
||||
|
||||
getViewCenter() {
|
||||
const { viewScaleInfo, viewSizeInfo } = this.getViewInfo();
|
||||
return calcViewCenter({ viewScaleInfo, viewSizeInfo });
|
||||
}
|
||||
|
||||
$onBoardWatcherEvents() {
|
||||
this.#core.onBoardWatcherEvents();
|
||||
}
|
||||
|
||||
$offBoardWatcherEvents() {
|
||||
this.#core.offBoardWatcherEvents();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,7 +116,8 @@ export {
|
|||
deleteElementInList,
|
||||
deepResizeGroupElement,
|
||||
deepCloneElement,
|
||||
calcViewCenterContent
|
||||
calcViewCenterContent,
|
||||
calcViewCenter
|
||||
} from '@idraw/util';
|
||||
export { iDraw } from './idraw';
|
||||
export type { IDrawEvent, IDrawEventKeys } from './event';
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export function drawHTML(ctx: ViewContext2D, elem: Element<'html'>, opts: Render
|
|||
const { calculator, viewScaleInfo, viewSizeInfo, parentOpacity } = opts;
|
||||
const { x, y, w, h, angle } = calculator?.elementSize(elem, viewScaleInfo, viewSizeInfo) || elem;
|
||||
rotateElement(ctx, { x, y, w, h, angle }, () => {
|
||||
if (!content) {
|
||||
if (!content && !opts.loader.isDestroyed()) {
|
||||
opts.loader.load(elem as Element<'html'>, opts.elementAssets || {});
|
||||
}
|
||||
if (elem.type === 'html' && content) {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export function drawImage(ctx: ViewContext2D, elem: Element<'image'>, opts: Rend
|
|||
viewSizeInfo,
|
||||
parentOpacity,
|
||||
renderContent: () => {
|
||||
if (!content) {
|
||||
if (!content && !opts.loader.isDestroyed()) {
|
||||
opts.loader.load(elem as Element<'image'>, opts.elementAssets || {});
|
||||
}
|
||||
if (elem.type === 'image' && content) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export function drawSVG(ctx: ViewContext2D, elem: Element<'svg'>, opts: Renderer
|
|||
const { calculator, viewScaleInfo, viewSizeInfo, parentOpacity } = opts;
|
||||
const { x, y, w, h, angle } = calculator?.elementSize(elem, viewScaleInfo, viewSizeInfo) || elem;
|
||||
rotateElement(ctx, { x, y, w, h, angle }, () => {
|
||||
if (!content) {
|
||||
if (!content && !opts.loader.isDestroyed()) {
|
||||
opts.loader.load(elem as Element<'svg'>, opts.elementAssets || {});
|
||||
}
|
||||
if (elem.type === 'svg' && content) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import type { Data, BoardRenderer, RendererOptions, RendererEventMap, RendererDr
|
|||
export class Renderer extends EventEmitter<RendererEventMap> implements BoardRenderer {
|
||||
#opts: RendererOptions;
|
||||
#loader: Loader = new Loader();
|
||||
#hasDestroyed: boolean = false;
|
||||
|
||||
constructor(opts: RendererOptions) {
|
||||
super();
|
||||
|
|
@ -14,10 +15,16 @@ export class Renderer extends EventEmitter<RendererEventMap> implements BoardRen
|
|||
this.#init();
|
||||
}
|
||||
|
||||
isDestroyed() {
|
||||
return this.#hasDestroyed;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.clear();
|
||||
this.#opts = null as any;
|
||||
this.#loader.destroy();
|
||||
this.#loader = null as any;
|
||||
this.#hasDestroyed = true;
|
||||
}
|
||||
|
||||
#init() {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ export class Loader extends EventEmitter<LoaderEventMap> implements RendererLoad
|
|||
#loadFuncMap: Record<LoadElementType | string, LoadFunc<LoadElementType, LoadContent>> = {};
|
||||
#currentLoadItemMap: LoadItemMap = {};
|
||||
#storageLoadItemMap: LoadItemMap = {};
|
||||
#hasDestroyed: boolean = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
|
@ -60,7 +61,13 @@ export class Loader extends EventEmitter<LoaderEventMap> implements RendererLoad
|
|||
});
|
||||
}
|
||||
|
||||
isDestroyed() {
|
||||
return this.#hasDestroyed;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.#hasDestroyed = true;
|
||||
this.clear();
|
||||
this.#loadFuncMap = null as any;
|
||||
this.#currentLoadItemMap = null as any;
|
||||
this.#storageLoadItemMap = null as any;
|
||||
|
|
@ -99,28 +106,32 @@ export class Loader extends EventEmitter<LoaderEventMap> implements RendererLoad
|
|||
#emitLoad(item: LoadItem) {
|
||||
const assetId = getAssetIdFromElement(item.element);
|
||||
const storageItem = this.#storageLoadItemMap[assetId];
|
||||
if (storageItem) {
|
||||
if (storageItem.startTime < item.startTime) {
|
||||
if (!this.#hasDestroyed) {
|
||||
if (storageItem) {
|
||||
if (storageItem.startTime < item.startTime) {
|
||||
this.#storageLoadItemMap[assetId] = item;
|
||||
this.trigger('load', { ...item, countTime: item.endTime - item.startTime });
|
||||
}
|
||||
} else {
|
||||
this.#storageLoadItemMap[assetId] = item;
|
||||
this.trigger('load', { ...item, countTime: item.endTime - item.startTime });
|
||||
}
|
||||
} else {
|
||||
this.#storageLoadItemMap[assetId] = item;
|
||||
this.trigger('load', { ...item, countTime: item.endTime - item.startTime });
|
||||
}
|
||||
}
|
||||
|
||||
#emitError(item: LoadItem) {
|
||||
const assetId = getAssetIdFromElement(item.element);
|
||||
const storageItem = this.#storageLoadItemMap?.[assetId];
|
||||
if (storageItem) {
|
||||
if (storageItem.startTime < item.startTime) {
|
||||
if (!this.#hasDestroyed) {
|
||||
if (storageItem) {
|
||||
if (storageItem.startTime < item.startTime) {
|
||||
this.#storageLoadItemMap[assetId] = item;
|
||||
this.trigger('error', { ...item, countTime: item.endTime - item.startTime });
|
||||
}
|
||||
} else {
|
||||
this.#storageLoadItemMap[assetId] = item;
|
||||
this.trigger('error', { ...item, countTime: item.endTime - item.startTime });
|
||||
}
|
||||
} else {
|
||||
this.#storageLoadItemMap[assetId] = item;
|
||||
this.trigger('error', { ...item, countTime: item.endTime - item.startTime });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -130,16 +141,19 @@ export class Loader extends EventEmitter<LoaderEventMap> implements RendererLoad
|
|||
|
||||
this.#currentLoadItemMap[assetId] = item;
|
||||
const loadFunc = this.#loadFuncMap[element.type];
|
||||
if (typeof loadFunc === 'function') {
|
||||
if (typeof loadFunc === 'function' && !this.#hasDestroyed) {
|
||||
item.startTime = Date.now();
|
||||
loadFunc(element, assets)
|
||||
.then((result) => {
|
||||
item.content = result.content;
|
||||
item.endTime = Date.now();
|
||||
item.status = 'load';
|
||||
this.#emitLoad(item);
|
||||
if (!this.#hasDestroyed) {
|
||||
item.content = result.content;
|
||||
item.endTime = Date.now();
|
||||
item.status = 'load';
|
||||
this.#emitLoad(item);
|
||||
}
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`Load element source "${item.source}" fail`, err, element);
|
||||
item.endTime = Date.now();
|
||||
item.status = 'error';
|
||||
|
|
@ -159,6 +173,9 @@ export class Loader extends EventEmitter<LoaderEventMap> implements RendererLoad
|
|||
}
|
||||
|
||||
load(element: Element<LoadElementType>, assets: ElementAssets) {
|
||||
if (this.#hasDestroyed === true) {
|
||||
return;
|
||||
}
|
||||
if (this.#isExistingErrorStorage(element)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ export interface BoardRenderer extends UtilEventEmitter<RendererEventMap> {
|
|||
drawData(data: Data, opts: RendererDrawOptions): void;
|
||||
scale(num: number): void;
|
||||
destroy(): void;
|
||||
isDestroyed(): boolean;
|
||||
getLoader(): RendererLoader;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ export type CursorType =
|
|||
| 'resize-bottom-left'
|
||||
| 'resize-bottom-right'
|
||||
| 'drag-default'
|
||||
| 'drag-active';
|
||||
| 'drag-active'
|
||||
| 'default';
|
||||
|
||||
export interface CoreEventCursor {
|
||||
type: CursorType | string | null;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export interface RendererLoader extends UtilEventEmitter<LoaderEventMap> {
|
|||
getLoadItemMap(): LoadItemMap;
|
||||
setLoadItemMap(itemMap: LoadItemMap): void;
|
||||
destroy(): void;
|
||||
isDestroyed(): boolean;
|
||||
}
|
||||
|
||||
export interface RendererDrawOptions {
|
||||
|
|
|
|||
50
packages/util/__tests__/_assets/base.ts
Normal file
50
packages/util/__tests__/_assets/base.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
export const imageBase64 = `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAALNElEQVR4nO2cXchtRRnHf89a+33fc47Hk6WlpJ68yfQiSyEjE40oQUqvkkorTwWKkPSB0EUXURdFF2Z4EQZpkqZEBUYQROUHFkomqEdE0TJLJT9OejzvOe/X3vN0MetjZtastWf2u08Q7D9s9lozzzzzn2fNPPPMs/b7wgILLLDAAgsssMACCyywwP8akir4ljt/3jYSQKvWyoeByxUuBPYKrCs8BvxW4DaF5712gGpH/TcFrlN42iXUiAmIvRkBRxReBA4IvAI8i/CUKk8IvIpU7dQZoNg+w8F2abT4z6c/OVDbYpQkFcf7UX4KnB4QWRY4H+F8lO8ANwPXAFu1QG1/B3uB3cDZ9XPxZAPhjiFsvQKPotyHcBdw7wxjykYxUyvlq8CDwOl1kRAYprKEwBcF/ga8HVc4AnGqXEP2LRN16qtJ+l7gy6LcI3bmf4XtTZKpSDdgzVK4Cvh+XRydMU5ldX2qwAPAsY2cOB+6y8ktC+sU14P4hnRwMnADcBDlmrRB5iPdgAqinIHyoz7SYZlrAIW9KLc3I1a79FS7D8E1UEyfO9s7M99BVb4L+KHAo1hXMVckG7AaxDdcojHSrtEk+ACXKlwUGiK6cUTKQrlw5tflGi8/C3gOuDTSxcxINqDaJfEJl1RV3qAzIInKXRm2C2f0NIOGfbk6+sod/BrrG+eCHB/4UWCHu6ymLl/166r6CwQK13/1LUMVX2cdorhG6TPcEBRuULg2o0kv0pew4dyQBfT4qSDmEv/+FFXOjMSCnRnstdNWr/vwwuupeto2N4pyiagS+6QiY4vXHf66i7tuge42GcooK0BnavaWEanr0HP6k4qfqF/fZXInyhk4wX4ucnbhw5iKiKkY1df1d30Nfh2BDHoE1M4Md0268jj3SrcP974qk5BfTN7Tp8cAN3llEac5hJwl/ICoIqaa5sa9rpZJWB4t02fF8KQYUFW0imWc+koe/7q+7+qz7Z1vieoLrqt7jH5MVL8w6xLOmIH6B5TV6NNyA7pOeUf+3safOZ/htn36Wz19ddM+Vdtvi7Li8UlEsg8U5WXQOxS5CjQa3Nb3Q3Ugt7Q6G6Z/xyYCntFW0IiwA+V4hZMFRrWiRsbZpe2toBU3zyVW3ajEeQInK3wJuD7VHs0YUgXf+pM7AE4BngLd1UtlGLchfC40bAJORDkL+AhwBTYmnTeexjnbv/L5y5Ma5SxhUH0eo/u6y0pTPvtRvRLHB2XgJeD3wNdRPQWjl2P0YGK/qZ93iuolR80HNv4BfiFGP9M698Ax1w7fL39EjJ5X7Rot6ZkgAHeCnihG/xLnEOMDcc7tPUYvw34ns8k4iVSpE4ufoZwuxtwvbihhoCHQln0L5Wxg1UawEurKhz0nbmD4AEYfp8Mh4KMuJ43wbe4vxrDThkFpSA+kJ1arjgq7e030aVUuEPTdAp8CzgPehrIFPAX8DuGXKvIGAlpKRbZlp2WJiGQ98XpnKCYKqgbrEx8dbJOu/gTgXOC+1AbJBhwdHgOKGRWY5dLO3VLAsB/V/Y2gm0Ao2mRfsWGQzYkduABGMSuG8Z5lKCTNJxa2XbFprNFtX48BtwL7WsG+BFcSLuRoGNDyEYpNQ7FlMGVhDVg04UmnQTGxy0MmBpkYK9PEFkK5NkaArT3LaCnIZGDQhYAq5doEJooWNDYSuBVkXytsInyGBuZFFOfkGD/dgHUMVvkuGRtkrCDSluF0rSDVcU8FKIqKaktORwXF+oQls8l41whdti45NKSWgoyVcnULMaBFdWxrcZ+g/1A4reWg0XkYn5tuib7n6BjQYhnYCRgKGe5GqiG06RSphr3qimkpFFsTlg9OmKyUTHaO0KXCGlFBR3Zmjt7YhIlCKaHxajwocFrVtUsjRm0Ip2Ez1/8cFrPIWMIKcDVwI9YIjf1s/O9H/y3q8wHHgj6DDVY9kXoGl2sTyrUJ4z3LTHaNqM/Io9c3kbFpN6I4Hqk2s84JqC4bMlxQfypzN6B96svVvNvtV4q3NH2inofcPTQMFevnRq9voAJmR8nSgY3GeFM2msddNm7fXU5uqcuvabcX+PNQZzXSz8KD5KfVuQNK8y9Lr2+ihVCMJ2jaLv3v1iCpx0t3s/H0vyOJJFkzsHY8055lDNpzPdSfUoypQpykFq/qLP3E2xyX2DhnBsY6DGQS9GRHZ+lB9qrAEexrzO3iTamCGTOwL9ojWh6DkvdTiExjrwocUtjlprNiOofySFWfO1M73bYPzDnR5p5+M+UVMLEXS306B0Kc5NNwugHr4HYbOQDIS8Jk5huEWX/rsw1kHuVoX9gMBlk2HEHqbxqBLJvkpbwMyKTDx+1fQcRm1Nr6+nip7tpOfhAZM3BgFxaCSa/td1M3wwzOc4Ij0KVhZdZyzfPUgFfDU7YiCvo6TYSpx+6cZcnfeZPtJ9UZOn0X3i3IHttfy6y7YcT4hyNR77g5hGQDFqY+ordINUa226xmhyipQTTAMaArbX/hgXIaLy8eXEuluu0wZu4QMEXB0sENyrUxmyfsnJ7qsjh+jiwOpApmG3AIQ6eRodjMFTJFwdIbm4xWx4hRlg6ss3X8Dttw2IgnTiWYgIpn8k89ZogD+0LpeG1qnfV5BaPVTUaHttCiQEdCMTYsHVhnfNxKk8nuwZnDPafvSAIvpMrOYMD21bUMEKtrW5cuzXWMMSKMVrcoj4zRsqiVoGWBjJXRwU3Gx1abbCS8ETinf4YPpVW7vASZvwHdMKVNXQ2di8NUUr8P1QLKw2PKtYnN+YXPpZqJo8NbTHaNqp9juLupAHxwun+OZSw7vF4EfW6qqppaquDR2kS0EIr1CeW6Y7yOUJXW3zKUR8aYldKzgaAXk5yCmrqU93NUjnL1E89OpwwphWKsFBumyUoPorBGFARdEpfL3H6yCzycI5x/Fo74j64rDGWogjpfp4ogY+OJTYMWYt/ySUH1XuYKVC9qlHT6CchF3bZTKHJPGhOL7LOweA63b1ZGkpQRtymofTDZaZrm3cj7gNu9vga5xLh6hS+hen8OlZnSWdvxg95uV/9idDaFl2D0rjn75F8BGzkNthVI50VXbZvmOs94gt0ozkP1apQL8tjUodeg1C39VXHkvtZ03ni1tOaAz2L/BuUFJxOwE1gCjgHejPIu4KT+/qY9yqkr6DdkbiAw53ci28CHmPNfEM2A787SKOcnvhX68zHdlJFEXFxnC3IST16ms9NPPP00lB/ye9coIwW4GeQBZkC+D/R4+2XdH2drm5aPTFyJ6YqFHJViX79G7BDZ0TVYuuKXAf9CuHbWlZXtA6N/YKPh7KGVUfBPwo5MM5Bu2tOGJOJ/N7J9XNzgOjy2SVPsh2LsQyU5/xdipjiwt7L3KbbHwFg6wdcZu55W7993XUlXtjLndcDd2/Hr2/CBkacbzXq47cI9PCafq7PDtC9NHwreAJL9Zw0h8pdwlNDwzJsutx2ds+jgB8DX5hFR5L/WHIK0X/6rQ6d8qHntwtSRHdpsA2VC3EXXb1er62sUbkoYTRKyf6Hq3vYdKzWQrYU75d2mfl+h0ZyHEjNwp72jRoSXgY+jPDTPnNz83+SL/y31ZU+er/5Eo4/QkIFL64RIYZ/tHnS9wknAQxkjSUL+Ek55ek4EocGgo5BIlfjXEuiqf1DQLNtuN68h/Fjgeyrpb9lyMdMSjkRfvgGcWTjk92K2DSPC5jvy4JoY29atoTwswp+APyrcTUZmeVbkzsANrEM+JNLuy+EBwR25sxT3iHDIE20VrFcyh9yl6+gXYAyMxf7rp9ewrx6fVOEZ4AmBvyIcSR7PAgsssMACCyywwAILLLDA/y3+C0es7cRViJjeAAAAAElFTkSuQmCC`;
|
||||
|
||||
export const svg = `<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M336 421m-48 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0Z" fill="#1296db"></path><path d="M688 421m-48 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0Z" fill="#1296db"></path><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64z m263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2-44.3-18.7-84.1-45.6-118.3-79.8-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8c18.7-44.3 45.6-84.1 79.8-118.3 34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2 44.3 18.7 84.1 45.6 118.3 79.8 34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8c-18.7 44.3-45.6 84.1-79.8 118.2z" fill="#1296db"></path><path d="M664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-0.3-4.2-3.9-7.4-8.1-7.4H360c-4.6 0-8.2 3.8-8 8.4 4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6c0.2-4.6-3.4-8.4-8-8.4z" fill="#1296db"></path></svg>`;
|
||||
|
||||
export const html = `
|
||||
<style>
|
||||
.btn-box {
|
||||
margin: 10px 0;
|
||||
}
|
||||
.btn {
|
||||
line-height: 1.5715;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
font-weight: 400;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
background-image: none;
|
||||
border: 1px solid transparent;
|
||||
box-shadow: 0 2px #00000004;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
height: 32px;
|
||||
padding: 4px 15px;
|
||||
font-size: 14px;
|
||||
border-radius: 2px;
|
||||
color: #000000d9;
|
||||
background: #fff;
|
||||
border-color: #d9d9d9;
|
||||
}
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background: #1890ff;
|
||||
border-color: #1890ff;
|
||||
text-shadow: 0 -1px 0 rgb(0 0 0 / 12%);
|
||||
box-shadow: 0 2px #0000000b;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<div class="btn-box">
|
||||
<button class="btn">
|
||||
<span>Button</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="btn-box">
|
||||
<button class="btn btn-primary">
|
||||
<span>Button Primary</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import {
|
||||
deepClone
|
||||
} from '../../src/lib/data';
|
||||
|
||||
import { deepClone, filterCompactData } from '../../src/lib/data';
|
||||
import { imageBase64, html, svg } from '../_assets/base';
|
||||
import type { Data } from '@idraw/types';
|
||||
|
||||
describe('@idraw/util: lib/data', () => {
|
||||
const json = {
|
||||
|
|
@ -12,12 +11,12 @@ describe('@idraw/util: lib/data', () => {
|
|||
{
|
||||
num: 1,
|
||||
str: 'a',
|
||||
bool: false,
|
||||
bool: false
|
||||
},
|
||||
{
|
||||
num: 2,
|
||||
str: 'b',
|
||||
bool: false,
|
||||
bool: false
|
||||
}
|
||||
],
|
||||
json: {
|
||||
|
|
@ -27,10 +26,10 @@ describe('@idraw/util: lib/data', () => {
|
|||
json: {
|
||||
num: 11,
|
||||
str: 'bbbb',
|
||||
bool: false,
|
||||
bool: false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const json2 = deepClone(json);
|
||||
json2.json.json.num *= 2;
|
||||
|
|
@ -41,7 +40,180 @@ describe('@idraw/util: lib/data', () => {
|
|||
expect(result).toStrictEqual(json2);
|
||||
});
|
||||
|
||||
|
||||
test('filterCompactData', () => {
|
||||
const originData: Data = {
|
||||
elements: [
|
||||
{
|
||||
uuid: 'b37213ce-d711-cbb3-51ac-d8081c19f127',
|
||||
type: 'image',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
src: imageBase64
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: '39308517-e10f-76df-43a9-50ed7295e61e',
|
||||
type: 'svg',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
svg: svg
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'ef934ab7-a32e-040c-9ac0-ed193405e6e4',
|
||||
type: 'html',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
html: html
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: '063e3a80-1ede-7912-f919-975e34a9bd01',
|
||||
type: 'group',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
children: [
|
||||
{
|
||||
uuid: 'e0889472-1f16-d6cd-3c7a-4b827d52279d',
|
||||
type: 'image',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
src: imageBase64
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'b60e64e8-833e-e112-d7eb-1ab6e7d6870c',
|
||||
type: 'svg',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
svg: svg
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: '61f2a61e-cdd5-ae36-983f-686ba8e35973',
|
||||
type: 'html',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
html: html
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
const data = deepClone(originData);
|
||||
const compactData = filterCompactData(data);
|
||||
|
||||
const expectData: Data = {
|
||||
elements: [
|
||||
{
|
||||
uuid: 'b37213ce-d711-cbb3-51ac-d8081c19f127',
|
||||
type: 'image',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: { src: '@assets/1919ff71-124e-2766-23bb-9a251bf3241c' }
|
||||
},
|
||||
{
|
||||
uuid: '39308517-e10f-76df-43a9-50ed7295e61e',
|
||||
type: 'svg',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: { svg: '@assets/b9b92016-5290-54e8-9668-807574952823' }
|
||||
},
|
||||
{
|
||||
uuid: 'ef934ab7-a32e-040c-9ac0-ed193405e6e4',
|
||||
type: 'html',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: { html: '@assets/34017fa0-2d48-2506-3464-238f34642b5c' }
|
||||
},
|
||||
{
|
||||
uuid: '063e3a80-1ede-7912-f919-975e34a9bd01',
|
||||
type: 'group',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
children: [
|
||||
{
|
||||
uuid: 'e0889472-1f16-d6cd-3c7a-4b827d52279d',
|
||||
type: 'image',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: { src: '@assets/1919ff71-124e-2766-23bb-9a251bf3241c' }
|
||||
},
|
||||
{
|
||||
uuid: 'b60e64e8-833e-e112-d7eb-1ab6e7d6870c',
|
||||
type: 'svg',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: { svg: '@assets/b9b92016-5290-54e8-9668-807574952823' }
|
||||
},
|
||||
{
|
||||
uuid: '61f2a61e-cdd5-ae36-983f-686ba8e35973',
|
||||
type: 'html',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: { html: '@assets/34017fa0-2d48-2506-3464-238f34642b5c' }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
assets: {
|
||||
'@assets/1919ff71-124e-2766-23bb-9a251bf3241c': {
|
||||
type: 'image',
|
||||
value: imageBase64
|
||||
},
|
||||
'@assets/b9b92016-5290-54e8-9668-807574952823': {
|
||||
type: 'svg',
|
||||
value: svg
|
||||
},
|
||||
'@assets/34017fa0-2d48-2506-3464-238f34642b5c': {
|
||||
type: 'html',
|
||||
value: html
|
||||
}
|
||||
}
|
||||
};
|
||||
expect(compactData).toStrictEqual(expectData);
|
||||
|
||||
const data2: Data = deepClone<Data>(expectData);
|
||||
const compactData2 = filterCompactData(data2);
|
||||
expect(compactData2).toStrictEqual(expectData);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -74,4 +74,4 @@ export {
|
|||
updateElementInList
|
||||
} from './lib/handle-element';
|
||||
export { deepResizeGroupElement } from './lib/resize-element';
|
||||
export { calcViewCenterContent } from './lib/view-content';
|
||||
export { calcViewCenterContent, calcViewCenter } from './lib/view-content';
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ export function filterCompactData(data: Data, opts?: { loadItemMap?: LoadItemMap
|
|||
type: 'image',
|
||||
value: loadItemMap[src].source as string
|
||||
};
|
||||
} else {
|
||||
} else if (!assets[src]) {
|
||||
const assetUUID = createAssetId(src);
|
||||
if (!assets[assetUUID]) {
|
||||
assets[assetUUID] = {
|
||||
|
|
@ -131,34 +131,40 @@ export function filterCompactData(data: Data, opts?: { loadItemMap?: LoadItemMap
|
|||
}
|
||||
} else if (elem.type === 'svg') {
|
||||
const svg = (elem as Element<'svg'>).detail.svg;
|
||||
const assetUUID = createAssetId(svg);
|
||||
|
||||
if (isAssetId(svg) && !assets[svg] && loadItemMap[svg] && typeof loadItemMap[svg]?.source === 'string') {
|
||||
assets[svg] = {
|
||||
type: 'svg',
|
||||
value: loadItemMap[svg].source as string
|
||||
};
|
||||
} else if (!assets[assetUUID]) {
|
||||
assets[assetUUID] = {
|
||||
type: 'svg',
|
||||
value: svg
|
||||
};
|
||||
} else if (!assets[svg]) {
|
||||
const assetUUID = createAssetId(svg);
|
||||
if (!assets[assetUUID]) {
|
||||
assets[assetUUID] = {
|
||||
type: 'svg',
|
||||
value: svg
|
||||
};
|
||||
}
|
||||
(elem as Element<'svg'>).detail.svg = assetUUID;
|
||||
}
|
||||
(elem as Element<'svg'>).detail.svg = assetUUID;
|
||||
} else if (elem.type === 'html') {
|
||||
const html = (elem as Element<'html'>).detail.html;
|
||||
const assetUUID = createAssetId(html);
|
||||
|
||||
if (isAssetId(html) && !assets[html] && loadItemMap[html] && typeof loadItemMap[html]?.source === 'string') {
|
||||
assets[html] = {
|
||||
type: 'html',
|
||||
value: loadItemMap[html].source as string
|
||||
};
|
||||
} else if (!assets[assetUUID]) {
|
||||
assets[assetUUID] = {
|
||||
type: 'html',
|
||||
value: html
|
||||
};
|
||||
} else if (!assets[html]) {
|
||||
const assetUUID = createAssetId(html);
|
||||
if (!assets[assetUUID]) {
|
||||
assets[assetUUID] = {
|
||||
type: 'html',
|
||||
value: html
|
||||
};
|
||||
}
|
||||
(elem as Element<'html'>).detail.html = assetUUID;
|
||||
}
|
||||
(elem as Element<'html'>).detail.html = assetUUID;
|
||||
} else if (elem.type === 'group' && Array.isArray((elem as Element<'group'>).detail.children)) {
|
||||
const groupAssets = (elem as Element<'group'>).detail.assets || {};
|
||||
Object.keys(groupAssets).forEach((assetId) => {
|
||||
|
|
|
|||
|
|
@ -55,6 +55,10 @@ export class EventEmitter<T extends Record<string, any>> implements UtilEventEmi
|
|||
}
|
||||
|
||||
destroy() {
|
||||
this.clear();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.#listeners.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { Data, ViewSizeInfo, Element, ElementSize } from 'idraw';
|
||||
import type { Data, ViewSizeInfo, Element, ElementSize, ViewScaleInfo, PointSize } from '@idraw/types';
|
||||
import { rotateElementVertexes } from './rotate';
|
||||
import {} from './view-calc';
|
||||
import { formatNumber } from './number';
|
||||
|
|
@ -12,12 +12,12 @@ interface ViewCenterContentResult {
|
|||
export function calcViewCenterContent(data: Data, opts: { viewSizeInfo: ViewSizeInfo }): ViewCenterContentResult {
|
||||
let offsetX: number = 0;
|
||||
let offsetY: number = 0;
|
||||
let scale: number = 0;
|
||||
let scale: number = 1;
|
||||
|
||||
let contentX: number = 0;
|
||||
let contentY: number = 0;
|
||||
let contentW: number = 0;
|
||||
let contentH: number = 0;
|
||||
let contentX: number = data?.elements?.[0]?.x || 0;
|
||||
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;
|
||||
|
||||
|
|
@ -56,8 +56,8 @@ export function calcViewCenterContent(data: Data, opts: { viewSizeInfo: ViewSize
|
|||
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;
|
||||
offsetX = (contentW * scale - width) / 2 / scale + contentX;
|
||||
offsetY = (contentH * scale - height) / 2 / scale + contentY;
|
||||
}
|
||||
|
||||
const result: ViewCenterContentResult = {
|
||||
|
|
@ -65,5 +65,24 @@ export function calcViewCenterContent(data: Data, opts: { viewSizeInfo: ViewSize
|
|||
offsetY: formatNumber(offsetY, { decimalPlaces: 0 }),
|
||||
scale
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function calcViewCenter(opts?: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): PointSize {
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
|
||||
if (opts) {
|
||||
const { viewScaleInfo, viewSizeInfo } = opts;
|
||||
const { offsetLeft, offsetTop, scale } = viewScaleInfo;
|
||||
const { width, height } = viewSizeInfo;
|
||||
x = 0 - offsetLeft + width / scale / 2;
|
||||
y = 0 - offsetTop + height / scale / 2;
|
||||
}
|
||||
const p: PointSize = {
|
||||
x,
|
||||
y
|
||||
};
|
||||
return p;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue