From e8332bcf0da726316f3ebf31cd8e660163a786ba Mon Sep 17 00:00:00 2001 From: ZhangWei Date: Wed, 12 Mar 2025 21:29:16 +0800 Subject: [PATCH] feat(docs-drawing-ui): optimize float-dom on doc canvas (#4824) --- .../core/src/types/interfaces/i-drawing.ts | 1 + ...c-drawing-transformer-update.controller.ts | 10 ++- .../controllers/doc-float-dom.controller.ts | 66 +++++-------------- .../drawing-popup-menu.controller.ts | 10 +-- .../doc-drawing-update.render-controller.ts | 17 +++-- .../src/services/drawing-render.service.ts | 2 +- packages/engine-render/src/shape/rect.ts | 9 +++ .../services/dom/canvas-dom-layer.service.ts | 1 + .../ui/src/views/components/dom/FloatDom.tsx | 1 + 9 files changed, 54 insertions(+), 63 deletions(-) diff --git a/packages/core/src/types/interfaces/i-drawing.ts b/packages/core/src/types/interfaces/i-drawing.ts index af3e8b604b..b6c7cfe4a4 100644 --- a/packages/core/src/types/interfaces/i-drawing.ts +++ b/packages/core/src/types/interfaces/i-drawing.ts @@ -115,6 +115,7 @@ export interface IDrawingParam extends IDrawingSearch { // The default value is BooleanNumber.FALSE. if it's true, Please use transforms. isMultiTransform?: BooleanNumber; groupId?: string; + allowTransform?: boolean; } // #endregion diff --git a/packages/docs-drawing-ui/src/controllers/doc-drawing-transformer-update.controller.ts b/packages/docs-drawing-ui/src/controllers/doc-drawing-transformer-update.controller.ts index b5bc3016c7..6fcff98e25 100644 --- a/packages/docs-drawing-ui/src/controllers/doc-drawing-transformer-update.controller.ts +++ b/packages/docs-drawing-ui/src/controllers/doc-drawing-transformer-update.controller.ts @@ -130,7 +130,10 @@ export class DocDrawingTransformerController extends Disposable { const drawingData = documentDataModel?.getSnapshot().drawings?.[drawing.drawingId]; if (drawingData?.layoutType === PositionedObjectLayoutType.INLINE) { - (object as Image).setOpacity(0.2); + try { + (object as Image).setOpacity(0.2); + } catch (e) { + } } if (drawingData != null) { @@ -202,7 +205,10 @@ export class DocDrawingTransformerController extends Disposable { const drawingCache = this._transformerCache.get(drawing?.drawingId); if (drawingCache?.drawing.layoutType === PositionedObjectLayoutType.INLINE) { - (object as Image).setOpacity(1); + try { + (object as Image).setOpacity(1); + } catch (e) { + } } } diff --git a/packages/docs-drawing-ui/src/controllers/doc-float-dom.controller.ts b/packages/docs-drawing-ui/src/controllers/doc-float-dom.controller.ts index d3fd968b61..4e042689dc 100644 --- a/packages/docs-drawing-ui/src/controllers/doc-float-dom.controller.ts +++ b/packages/docs-drawing-ui/src/controllers/doc-float-dom.controller.ts @@ -18,13 +18,13 @@ import type { DocumentDataModel, IDisposable, IDrawingSearch, Nullable } from '@ import type { IDocFloatDom } from '@univerjs/docs-drawing'; import type { ISetDocZoomRatioOperationParams } from '@univerjs/docs-ui'; import type { IDocFloatDomDataBase } from '@univerjs/drawing'; -import type { BaseObject, IRender, Rect } from '@univerjs/engine-render'; +import type { IRender, Rect } from '@univerjs/engine-render'; import type { IFloatDomLayout } from '@univerjs/ui'; import type { IInsertDrawingCommandParams } from '../commands/commands/interfaces'; import { Disposable, DisposableCollection, DrawingTypeEnum, fromEventSubject, generateRandomId, ICommandService, Inject, IUniverInstanceService, ObjectRelativeFromH, ObjectRelativeFromV, PositionedObjectLayoutType, toDisposable, UniverInstanceType } from '@univerjs/core'; import { DocSkeletonManagerService } from '@univerjs/docs'; import { docDrawingPositionToTransform, SetDocZoomRatioOperation, VIEWPORT_KEY } from '@univerjs/docs-ui'; -import { getDrawingShapeKeyByDrawingSearch, IDrawingManagerService } from '@univerjs/drawing'; +import { IDrawingManagerService } from '@univerjs/drawing'; import { DrawingRenderService } from '@univerjs/drawing-ui'; import { CURSOR_TYPE, IRenderManagerService } from '@univerjs/engine-render'; import { CanvasFloatDomService } from '@univerjs/ui'; @@ -32,10 +32,10 @@ import { BehaviorSubject, map, of, switchMap } from 'rxjs'; import { InsertDocDrawingCommand } from '../commands/commands/insert-doc-drawing.command'; function calcDocFloatDomPosition( - object: BaseObject, + object: Rect, renderUnit: IRender ): IFloatDomLayout { - const { top, left, width, height, angle } = object; + const { top, left, width, height, angle, opacity } = object; // const const scene = renderUnit.scene; const viewMain = scene.getViewport(VIEWPORT_KEY.VIEW_MAIN)!; @@ -49,11 +49,12 @@ function calcDocFloatDomPosition( endY: (top + height - viewportScrollY) * scaleY, width, height, - rotate: 0, + rotate: angle, absolute: { left: false, top: false, }, + opacity: opacity ?? 1, }; } @@ -64,6 +65,9 @@ interface ICanvasFloatDomInfo { unitId: string; } +interface IDocFloatDomParams extends IDocFloatDomDataBase { +} + export class DocFloatDomController extends Disposable { private _domLayerInfoMap = new Map(); @@ -86,7 +90,6 @@ export class DocFloatDomController extends Disposable { private _initialize() { this._drawingAddRemoveListener(); - this._imageUpdateListener(); this._initScrollAndZoomEvent(); } @@ -194,47 +197,6 @@ export class DocFloatDomController extends Disposable { }); } - private _imageUpdateListener() { - this.disposeWithMe( - this._drawingManagerService.update$.subscribe((params) => { - (params).forEach((param) => { - const { unitId, subUnitId, drawingId } = param; - - const drawingParam = this._drawingManagerService.getDrawingByParam(param) as IDocFloatDom; - - if (drawingParam == null) { - return; - } - - const { transform, drawingType } = drawingParam; - - if (drawingType !== DrawingTypeEnum.DRAWING_IMAGE) { - return; - } - - const renderObject = this._getSceneAndTransformerByDrawingSearch(unitId); - - if (renderObject == null) { - return; - } - const { scene } = renderObject; - - if (transform == null) { - return true; - } - - const drawingShapeKey = getDrawingShapeKeyByDrawingSearch({ unitId, subUnitId, drawingId }); - - const imageShape = scene.getObject(drawingShapeKey) as Rect; - - if (imageShape == null) { - return true; - } - }); - }) - ); - } - private _addHoverForRect(o: Rect) { this.disposeWithMe( toDisposable( @@ -311,7 +273,7 @@ export class DocFloatDomController extends Disposable { })); } - insertFloatDom(floatDom: IDocFloatDomDataBase, opts: { width?: number; height: number; drawingId?: string }) { + insertFloatDom(floatDom: IDocFloatDomParams, opts: { width?: number; height: number; drawingId?: string }) { const currentDoc = this._univerInstanceService.getCurrentUnitOfType(UniverInstanceType.UNIVER_DOC); if (!currentDoc) return false; const render = this._getSceneAndTransformerByDrawingSearch(currentDoc.getUnitId()); @@ -337,12 +299,12 @@ export class DocFloatDomController extends Disposable { }, angle: 0, }; - + const drawingId = opts.drawingId ?? generateRandomId(); const params: IInsertDrawingCommandParams = { unitId: currentDoc.getUnitId(), drawings: [ { - drawingId: opts.drawingId ?? generateRandomId(), + drawingId, drawingType: DrawingTypeEnum.DRAWING_DOM, subUnitId: currentDoc.getUnitId(), unitId: currentDoc.getUnitId(), @@ -355,6 +317,8 @@ export class DocFloatDomController extends Disposable { }, ], }; - this._commandService.executeCommand(InsertDocDrawingCommand.id, params); + this._commandService.syncExecuteCommand(InsertDocDrawingCommand.id, params); + + return drawingId; } } diff --git a/packages/docs-drawing-ui/src/controllers/drawing-popup-menu.controller.ts b/packages/docs-drawing-ui/src/controllers/drawing-popup-menu.controller.ts index 3b6d853d7b..bcc77ad512 100644 --- a/packages/docs-drawing-ui/src/controllers/drawing-popup-menu.controller.ts +++ b/packages/docs-drawing-ui/src/controllers/drawing-popup-menu.controller.ts @@ -16,7 +16,7 @@ import type { DocumentDataModel, IDisposable, Nullable } from '@univerjs/core'; import type { BaseObject, Scene } from '@univerjs/engine-render'; -import { FOCUSING_COMMON_DRAWINGS, IContextService, Inject, IUniverInstanceService, RxDisposable, SHEET_EDITOR_UNITS, toDisposable, UniverInstanceType } from '@univerjs/core'; +import { DrawingTypeEnum, FOCUSING_COMMON_DRAWINGS, IContextService, Inject, IUniverInstanceService, RxDisposable, SHEET_EDITOR_UNITS, toDisposable, UniverInstanceType } from '@univerjs/core'; import { DocCanvasPopManagerService } from '@univerjs/docs-ui'; import { IDrawingManagerService } from '@univerjs/drawing'; import { COMPONENT_IMAGE_POPUP_MENU, ImageCropperObject, ImageResetSizeOperation, OpenImageCropOperation } from '@univerjs/drawing-ui'; @@ -114,7 +114,7 @@ export class DocDrawingPopupMenuController extends RxDisposable { return; } - const { unitId, subUnitId, drawingId } = drawingParam; + const { unitId, subUnitId, drawingId, drawingType } = drawingParam; const popup = this._canvasPopManagerService.attachPopupToObject( object, { @@ -122,7 +122,7 @@ export class DocDrawingPopupMenuController extends RxDisposable { direction: 'horizontal', offset: [2, 0], extraProps: { - menuItems: this._getImageMenuItems(unitId, subUnitId, drawingId), + menuItems: this._getImageMenuItems(unitId, subUnitId, drawingId, drawingType), }, }, unitId @@ -176,14 +176,14 @@ export class DocDrawingPopupMenuController extends RxDisposable { ); } - private _getImageMenuItems(unitId: string, subUnitId: string, drawingId: string) { + private _getImageMenuItems(unitId: string, subUnitId: string, drawingId: string, drawingType: number) { return [ { label: 'image-popup.edit', index: 0, commandId: EditDocDrawingOperation.id, commandParams: { unitId, subUnitId, drawingId }, - disable: !!SHEET_EDITOR_UNITS.includes(unitId), + disable: !!SHEET_EDITOR_UNITS.includes(unitId) || drawingType === DrawingTypeEnum.DRAWING_DOM, }, { label: 'image-popup.delete', diff --git a/packages/docs-drawing-ui/src/controllers/render-controllers/doc-drawing-update.render-controller.ts b/packages/docs-drawing-ui/src/controllers/render-controllers/doc-drawing-update.render-controller.ts index dc762d082e..ddd629fd84 100644 --- a/packages/docs-drawing-ui/src/controllers/render-controllers/doc-drawing-update.render-controller.ts +++ b/packages/docs-drawing-ui/src/controllers/render-controllers/doc-drawing-update.render-controller.ts @@ -26,8 +26,8 @@ import { DocSelectionManagerService, DocSkeletonManagerService, RichTextEditingM import { IDocDrawingService } from '@univerjs/docs-drawing'; import { docDrawingPositionToTransform, DocSelectionRenderService } from '@univerjs/docs-ui'; import { DRAWING_IMAGE_ALLOW_IMAGE_LIST, DRAWING_IMAGE_ALLOW_SIZE, DRAWING_IMAGE_COUNT_LIMIT, DRAWING_IMAGE_HEIGHT_LIMIT, DRAWING_IMAGE_WIDTH_LIMIT, getDrawingShapeKeyByDrawingSearch, getImageSize, IDrawingManagerService, IImageIoService, ImageUploadStatusType } from '@univerjs/drawing'; - import { DocumentEditArea, IRenderManagerService } from '@univerjs/engine-render'; + import { ILocalFileService, IMessageService } from '@univerjs/ui'; import { debounceTime } from 'rxjs'; import { GroupDocDrawingCommand } from '../../commands/commands/group-doc-drawing.command'; @@ -351,13 +351,22 @@ export class DocDrawingUpdateRenderController extends Disposable implements IRen if (drawingShapes.length) { for (const shape of drawingShapes) { scene.detachTransformerFrom(shape); - (shape as Image).setOpacity(0.5); + try { + (shape as Image).setOpacity(0.5); + } catch (e) { + } if ( (isEditBody && drawing.isMultiTransform !== BooleanNumber.TRUE) || (!isEditBody && drawing.isMultiTransform === BooleanNumber.TRUE) ) { - scene.attachTransformerTo(shape); - (shape as Image).setOpacity(1); + if (drawing.allowTransform !== false) { + scene.attachTransformerTo(shape); + } + + try { + (shape as Image).setOpacity(1); + } catch (e) { + } } } } diff --git a/packages/drawing-ui/src/services/drawing-render.service.ts b/packages/drawing-ui/src/services/drawing-render.service.ts index abe4e515bc..438668a6a0 100644 --- a/packages/drawing-ui/src/services/drawing-render.service.ts +++ b/packages/drawing-ui/src/services/drawing-render.service.ts @@ -186,7 +186,7 @@ export class DrawingRenderService { } scene.addObject(rect, DRAWING_OBJECT_LAYER_INDEX); - if (this._drawingManagerService.getDrawingEditable()) { + if (this._drawingManagerService.getDrawingEditable() && param.allowTransform !== false) { scene.attachTransformerTo(rect); } diff --git a/packages/engine-render/src/shape/rect.ts b/packages/engine-render/src/shape/rect.ts index 029d0a7345..f1734afc4e 100644 --- a/packages/engine-render/src/shape/rect.ts +++ b/packages/engine-render/src/shape/rect.ts @@ -33,6 +33,7 @@ export class Rect extends Shape { override objectType = ObjectType.RECT; private _radius: number = 0; + private _opacity: number = 1; /** * For rendering, in many case object size is bigger than visual size for better user interaction. @@ -65,10 +66,18 @@ export class Rect extends Shape { return this._radius; } + get opacity() { + return this._opacity; + } + setObjectType(type: ObjectType) { this.objectType = type; } + setOpacity(opacity: number) { + this._opacity = opacity; + } + static override drawWith(ctx: UniverRenderingContext, props: IRectProps) { let { radius, width, height } = props; diff --git a/packages/ui/src/services/dom/canvas-dom-layer.service.ts b/packages/ui/src/services/dom/canvas-dom-layer.service.ts index a49cfa69d2..7f0b565c5d 100644 --- a/packages/ui/src/services/dom/canvas-dom-layer.service.ts +++ b/packages/ui/src/services/dom/canvas-dom-layer.service.ts @@ -26,6 +26,7 @@ export interface IFloatDomLayout extends IPosition { left: boolean; top: boolean; }; + opacity?: number; } export interface IFloatDom { diff --git a/packages/ui/src/views/components/dom/FloatDom.tsx b/packages/ui/src/views/components/dom/FloatDom.tsx index d4c0166cc5..39eb49a688 100644 --- a/packages/ui/src/views/components/dom/FloatDom.tsx +++ b/packages/ui/src/views/components/dom/FloatDom.tsx @@ -58,6 +58,7 @@ const FloatDomSingle = memo((props: { layer: IFloatDom; id: string }) => { domRef.current.style.transform = transformRef.current; domRef.current.style.top = `${topRef.current}px`; domRef.current.style.left = `${leftRef.current}px`; + domRef.current.style.opacity = `${position.opacity ?? 1}`; } });