Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(sheets-drawing-ui): float dom scroll performance optimize #3838

Merged
merged 5 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,22 @@
* limitations under the License.
*/

import type { IDisposable, IPosition, ITransformState, Nullable, Serializable, Worksheet } from '@univerjs/core';
import type { IDisposable, IPosition, ITransformState, Nullable, Serializable, Workbook, Worksheet } from '@univerjs/core';
import type { IDrawingJsonUndo1 } from '@univerjs/drawing';
import type { BaseObject, IBoundRectNoAngle, IRectProps, IRender, Scene, SpreadsheetSkeleton } from '@univerjs/engine-render';
import type { ISetFrozenMutationParams } from '@univerjs/sheets';
import type { IFloatDomData, ISheetDrawingPosition, ISheetFloatDom } from '@univerjs/sheets-drawing';
import type { IFloatDomLayout } from '@univerjs/ui';
import type { IInsertDrawingCommandParams } from '../commands/commands/interfaces';
import { Disposable, DisposableCollection, generateRandomId, ICommandService, Inject, IUniverInstanceService } from '@univerjs/core';
import { Disposable, DisposableCollection, fromEventSubject, generateRandomId, ICommandService, Inject, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
import { DrawingTypeEnum, getDrawingShapeKeyByDrawingSearch, IDrawingManagerService } from '@univerjs/drawing';

import { DRAWING_OBJECT_LAYER_INDEX, IRenderManagerService, Rect, SHEET_VIEWPORT_KEY } from '@univerjs/engine-render';
import { getSheetCommandTarget, SetFrozenMutation } from '@univerjs/sheets';
import { DrawingApplyType, ISheetDrawingService, SetDrawingApplyMutation } from '@univerjs/sheets-drawing';
import { ISheetSelectionRenderService, SetScrollOperation, SetZoomRatioOperation, SheetSkeletonManagerService } from '@univerjs/sheets-ui';
import { ISheetSelectionRenderService, SetZoomRatioOperation, SheetSkeletonManagerService, VIEWPORT_KEY } from '@univerjs/sheets-ui';
import { CanvasFloatDomService } from '@univerjs/ui';
import { BehaviorSubject, Subject } from 'rxjs';
import { BehaviorSubject, filter, map, Subject, switchMap } from 'rxjs';
import { InsertSheetDrawingCommand } from '../commands/commands/insert-sheet-drawing.command';

export interface ICanvasFloatDom {
Expand Down Expand Up @@ -371,29 +371,50 @@ export class SheetCanvasFloatDomManagerService extends Disposable {
}

private _scrollUpdateListener() {
this.disposeWithMe(this._commandService.onCommandExecuted((commandInfo) => {
const updateSheet = (unitId: string, subUnitId: string) => {
const renderObject = this._getSceneAndTransformerByDrawingSearch(unitId);
const map = this._ensureMap(unitId, subUnitId);
const ids = Array.from(map.keys());
const target = getSheetCommandTarget(this._univerInstanceService, { unitId, subUnitId });
const skeleton = this._renderManagerService.getRenderById(unitId)?.with(SheetSkeletonManagerService).getWorksheetSkeleton(subUnitId);
if (!renderObject || !target || !skeleton) {
return;
const updateSheet = (unitId: string, subUnitId: string) => {
const renderObject = this._getSceneAndTransformerByDrawingSearch(unitId);
const map = this._ensureMap(unitId, subUnitId);
const ids = Array.from(map.keys());
const target = getSheetCommandTarget(this._univerInstanceService, { unitId, subUnitId });
const skeleton = this._renderManagerService.getRenderById(unitId)?.with(SheetSkeletonManagerService).getWorksheetSkeleton(subUnitId);
if (!renderObject || !target || !skeleton) {
return;
}
ids.forEach((id) => {
const info = this._domLayerInfoMap.get(id);
if (info) {
const position = calcPosition(info.rect, renderObject.renderObject, skeleton.skeleton, target.worksheet);
info.position$.next(position);
}
ids.forEach((id) => {
const info = this._domLayerInfoMap.get(id);
if (info) {
const position = calcPosition(info.rect, renderObject.renderObject, skeleton.skeleton, target.worksheet);
info.position$.next(position);
}
});
};
if (commandInfo.id === SetScrollOperation.id) {
const params = (commandInfo.params) as any;
const { unitId, sheetId } = params;
updateSheet(unitId, sheetId);
} else if (commandInfo.id === SetZoomRatioOperation.id) {
});
};
let time = 0;

this.disposeWithMe(

this._univerInstanceService.getCurrentTypeOfUnit$<Workbook>(UniverInstanceType.UNIVER_SHEET)
.pipe(
filter((sheet) => !!sheet),
map((sheet) => {
const render = this._renderManagerService.getRenderById(sheet.getUnitId());
return render ? { render, unitId: sheet.getUnitId(), subUnitId: sheet.getActiveSheet().getSheetId() } : null;
}),
filter((render) => !!render),
switchMap((render) => fromEventSubject(render.render.scene.getViewport(VIEWPORT_KEY.VIEW_MAIN)!.onMouseWheel$)
.pipe(
map(() => ({ unitId: render.unitId, subUnitId: render.subUnitId }))
)
)
)
.subscribe(({ unitId, subUnitId }) => {
const now = performance.now();
time = now;
updateSheet(unitId, subUnitId);
})

);
this.disposeWithMe(this._commandService.onCommandExecuted((commandInfo) => {
if (commandInfo.id === SetZoomRatioOperation.id) {
const params = (commandInfo.params) as any;
const { unitId } = params;
const subUnitIds = Array.from(this._domLayerMap.get(unitId)?.keys() ?? []);
Expand Down
142 changes: 99 additions & 43 deletions packages/ui/src/views/components/dom/FloatDom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

import type { IFloatDom } from '../../../services/dom/canvas-dom-layer.service';
import { IUniverInstanceService, UniverInstanceType, useDependency } from '@univerjs/core';
import React, { memo } from 'react';
import React, { memo, useEffect, useMemo, useRef } from 'react';
import { distinctUntilChanged, first } from 'rxjs';
import { ComponentManager } from '../../../common';
import { useObservable } from '../../../components/hooks/observable';
import { CanvasFloatDomService } from '../../../services/dom/canvas-dom-layer.service';
Expand All @@ -25,55 +26,110 @@ import styles from './index.module.less';
const FloatDomSingle = memo((props: { layer: IFloatDom; id: string }) => {
const { layer, id } = props;
const componentManager = useDependency(ComponentManager);
const position = useObservable(layer.position$);
const size$ = useMemo(() => layer.position$.pipe(
distinctUntilChanged(
(prev, curr) => prev.absolute.left === curr.absolute.left &&
prev.absolute.top === curr.absolute.top &&
prev.endX - prev.startX === curr.endX - curr.startX &&
prev.endY - prev.startY === curr.endY - curr.startY
)
), [layer.position$]);

const position = useObservable(useMemo(() => layer.position$.pipe(first()), [layer.position$]));
const domRef = useRef<HTMLDivElement>(null);
const innerDomRef = useRef<HTMLDivElement>(null);
const transformRef = useRef<string>(`transform: rotate(${position?.rotate}deg) translate(${position?.startX}px, ${position?.startY}px)`);
const innerStyle = useRef<React.CSSProperties>({

});
const Component = typeof layer.componentKey === 'string' ? componentManager.get(layer.componentKey) : layer.componentKey;
const layerProps: any = {
const layerProps: any = useMemo(() => ({
data: layer.data,
...layer.props,
};
}), [layer.data, layer.props]);

useEffect(() => {
const subscription = layer.position$.subscribe((position) => {
transformRef.current = `rotate(${position.rotate}deg) translate(${position.startX}px, ${position.startY}px)`;
if (domRef.current) {
domRef.current.style.transform = transformRef.current;
}
});

const sizeSubscription = size$.subscribe((size) => {
if (domRef.current) {
domRef.current.style.width = `${size.endX - size.startX}px`;
domRef.current.style.height = `${size.endY - size.startY}px`;
}

if (innerDomRef.current) {
const style = {
width: `${size.width}px`,
height: `${size.height}px`,
left: `${size.absolute.left ? 0 : 'auto'}`,
top: `${size.absolute.top ? 0 : 'auto'}`,
right: `${size.absolute.left ? 'auto' : 0}`,
bottom: `${size.absolute.top ? 'auto' : 0}`,
};

return position
? (
innerDomRef.current.style.width = style.width;
innerDomRef.current.style.height = style.height;
innerDomRef.current.style.left = style.left;
innerDomRef.current.style.top = style.top;
innerDomRef.current.style.right = style.right;
innerDomRef.current.style.bottom = style.bottom;

innerStyle.current = style;
}
});
return () => {
subscription.unsubscribe();
sizeSubscription.unsubscribe();
};
}, [layer.position$, size$]);

const component = useMemo(() => Component ? <Component {...layerProps} /> : null, [Component, layerProps]);

if (!position) {
return null;
}

return (
<div
ref={domRef}
className={styles.floatDomWrapper}
style={{
position: 'absolute',
top: 0,
left: 0,
width: Math.max(position.endX - position.startX - 2, 0),
height: Math.max(position.endY - position.startY - 2, 0),
transform: transformRef.current,
overflow: 'hidden',
}}
onPointerMove={(e) => {
layer.onPointerMove(e.nativeEvent);
}}
onPointerDown={(e) => {
layer.onPointerDown(e.nativeEvent);
}}
onPointerUp={(e) => {
layer.onPointerUp(e.nativeEvent);
}}
onWheel={(e) => {
layer.onWheel(e.nativeEvent);
}}
>
<div
className={styles.floatDomWrapper}
style={{
position: 'absolute',
top: position.startY,
left: position.startX,
width: Math.max(position.endX - position.startX - 2, 0),
height: Math.max(position.endY - position.startY - 2, 0),
transform: `rotate(${position.rotate}deg)`,
overflow: 'hidden',
}}
onPointerMove={(e) => {
layer.onPointerMove(e.nativeEvent);
}}
onPointerDown={(e) => {
layer.onPointerDown(e.nativeEvent);
}}
onPointerUp={(e) => {
layer.onPointerUp(e.nativeEvent);
}}
onWheel={(e) => {
layer.onWheel(e.nativeEvent);
}}
id={id}
ref={innerDomRef}
className={styles.floatDom}
style={{ position: 'absolute', ...innerStyle.current }}
>
<div
id={id}
className={styles.floatDom}
style={{
width: position.width,
height: position.height,
position: 'absolute',
...(position.absolute.left) ? { left: 0 } : { right: 0 },
...(position.absolute.top) ? { top: 0 } : { bottom: 0 },
}}
>
{Component ? <Component {...layerProps} /> : null}
</div>
{component}
</div>
)
: null;
</div>
);
});

export const FloatDom = ({ unitId }: { unitId?: string }) => {
Expand Down
Loading