fix: drag block issue (#9902)

### Changed
- Added support for changing the preview offset during dragging.
- Fixed the preview rendering for embed block and surface-ref block
- Resolved an issue where the host element might be reused in certain cases, which could cause unexpected behavior
- Moved viewport-related constants and methods to a more appropriate location
This commit is contained in:
doouding
2025-02-05 07:25:53 +00:00
parent abeff8bb1a
commit 02122098c7
22 changed files with 177 additions and 138 deletions

View File

@@ -47,7 +47,6 @@ import { EdgelessPageKeyboardManager } from './edgeless-keyboard.js';
import type { EdgelessRootService } from './edgeless-root-service.js';
import { getBackgroundGrid, isCanvasElement } from './utils/query.js';
import { mountShapeTextEditor } from './utils/text.js';
import { fitToScreen } from './utils/viewport.js';
export class EdgelessRootBlockComponent extends BlockComponent<
RootBlockModel,
@@ -341,9 +340,7 @@ export class EdgelessRootBlockComponent extends BlockComponent<
const storedViewport = std.get(EditPropsStore).getStorage('viewport');
if (!storedViewport) {
fitToScreen(this.gfx.gfxElements, gfx.viewport, {
smooth: false,
});
this.gfx.fitToScreen();
return;
}

View File

@@ -23,7 +23,6 @@ import { query, state } from 'lit/decorators.js';
import type { EdgelessRootBlockWidgetName } from '../types.js';
import type { EdgelessRootService } from './edgeless-root-service.js';
import { getBackgroundGrid, isCanvasElement } from './utils/query.js';
import { fitToScreen } from './utils/viewport.js';
export class EdgelessRootPreviewBlockComponent
extends BlockComponent<
@@ -178,14 +177,6 @@ export class EdgelessRootPreviewBlockComponent
);
}
private _initViewport() {
const gfx = this.service.gfx;
fitToScreen(gfx.gfxElements, gfx.viewport, {
smooth: false,
});
}
private get _disableScheduleUpdate() {
const editorSetting = this.std.getOptional(EditorSettingProvider);
@@ -195,7 +186,6 @@ export class EdgelessRootPreviewBlockComponent
override connectedCallback() {
super.connectedCallback();
this._initViewport();
this.handleEvent('selectionChange', () => {
const surface = this.host.selection.value.find(
(sel): sel is SurfaceSelection => sel.is(SurfaceSelection)

View File

@@ -27,6 +27,9 @@ import {
GfxControllerIdentifier,
GfxExtensionIdentifier,
isGfxGroupCompatibleModel,
ZOOM_MAX,
ZOOM_MIN,
ZOOM_STEP,
} from '@blocksuite/block-std/gfx';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import { Bound, getCommonBound } from '@blocksuite/global/utils';
@@ -41,15 +44,7 @@ import {
createStickerMiddleware,
replaceIdMiddleware,
} from './services/template-middlewares.js';
import { FIT_TO_SCREEN_PADDING } from './utils/consts.js';
import { getCursorMode } from './utils/query.js';
import {
ZOOM_INITIAL,
ZOOM_MAX,
ZOOM_MIN,
ZOOM_STEP,
type ZoomAction,
} from './utils/zoom.js';
export class EdgelessRootService extends RootService implements SurfaceContext {
static override readonly flavour = RootBlockSchema.model.flavour;
@@ -288,34 +283,6 @@ export class EdgelessRootService extends RootService implements SurfaceContext {
return this.surface.getConnectors(id) as ConnectorElementModel[];
}
getFitToScreenData(
padding: [number, number, number, number] = [0, 0, 0, 0],
inputBounds?: Bound[]
) {
let bounds = [];
if (inputBounds && inputBounds.length) {
bounds = inputBounds;
} else {
this.blocks.forEach(block => {
bounds.push(Bound.deserialize(block.xywh));
});
const surfaceElementsBound = getCommonBound(this.elements);
if (surfaceElementsBound) {
bounds.push(surfaceElementsBound);
}
}
const bound = getCommonBound(bounds);
return this.viewport.getFitToScreenData(
bound,
padding,
ZOOM_INITIAL,
FIT_TO_SCREEN_PADDING
);
}
override mounted() {
super.mounted();
this._initSlotEffects();
@@ -372,12 +339,12 @@ export class EdgelessRootService extends RootService implements SurfaceContext {
}
}
setZoomByAction(action: ZoomAction) {
setZoomByAction(action: 'fit' | 'out' | 'reset' | 'in') {
if (this.locked) return;
switch (action) {
case 'fit':
this.zoomToFit();
this.gfx.fitToScreen();
break;
case 'reset':
this.viewport.smoothZoom(1.0);
@@ -439,9 +406,4 @@ export class EdgelessRootService extends RootService implements SurfaceContext {
this.selectionManager.set([]);
this.disposables.dispose();
}
zoomToFit() {
const { centerX, centerY, zoom } = this.getFitToScreenData();
this.viewport.setViewport(zoom, [centerX, centerY], true);
}
}

View File

@@ -67,7 +67,6 @@ import {
mountShapeTextEditor,
mountTextElementEditor,
} from '../utils/text.js';
import { fitToScreen } from '../utils/viewport.js';
import { CanvasElementEventExt } from './default-tool-ext/event-ext.js';
import type { DefaultToolExt } from './default-tool-ext/ext.js';
import { DefaultModeDragType } from './default-tool-ext/ext.js';
@@ -766,11 +765,7 @@ export class DefaultTool extends BaseTool {
if (this.doc.readonly) {
const viewport = this.gfx.viewport;
if (viewport.zoom === 1) {
// Fit to Screen
fitToScreen(
[...this.gfx.layer.blocks, ...this.gfx.layer.canvasElements],
this.gfx.viewport
);
this.gfx.fitToScreen();
} else {
// Zoom to 100% and Center
const [x, y] = viewport.toModelCoord(e.x, e.y);

View File

@@ -1,28 +0,0 @@
import type { GfxModel, Viewport } from '@blocksuite/block-std/gfx';
import { Bound, getCommonBound } from '@blocksuite/global/utils';
import { FIT_TO_SCREEN_PADDING } from './consts.js';
import { ZOOM_INITIAL } from './zoom.js';
export function fitToScreen(
elements: GfxModel[],
viewport: Viewport,
options: {
padding?: [number, number, number, number];
smooth?: boolean;
} = {
padding: [0, 0, 0, 0],
smooth: true,
}
) {
const elemBounds = elements.map(element => Bound.deserialize(element.xywh));
const commonBound = getCommonBound(elemBounds);
const { zoom, centerX, centerY } = viewport.getFitToScreenData(
commonBound,
options.padding,
ZOOM_INITIAL,
FIT_TO_SCREEN_PADDING
);
viewport.setViewport(zoom, [centerX, centerY], options.smooth);
}

View File

@@ -1,5 +0,0 @@
export type ZoomAction = 'fit' | 'out' | 'reset' | 'in';
export const ZOOM_MAX = 6.0;
export const ZOOM_MIN = 0.1;
export const ZOOM_STEP = 0.25;
export const ZOOM_INITIAL = 1.0;

View File

@@ -4,6 +4,7 @@ import {
ViewBarIcon,
} from '@blocksuite/affine-components/icons';
import { stopPropagation } from '@blocksuite/affine-shared/utils';
import { ZOOM_STEP } from '@blocksuite/block-std/gfx';
import { WithDisposable } from '@blocksuite/global/utils';
import { effect } from '@preact/signals-core';
import { baseTheme } from '@toeverything/theme';
@@ -11,7 +12,6 @@ import { css, html, LitElement, nothing, unsafeCSS } from 'lit';
import { property } from 'lit/decorators.js';
import type { EdgelessRootBlockComponent } from '../../edgeless/edgeless-root-block.js';
import { ZOOM_STEP } from '../../edgeless/utils/zoom.js';
export class EdgelessZoomToolbar extends WithDisposable(LitElement) {
static override styles = css`
@@ -87,6 +87,10 @@ export class EdgelessZoomToolbar extends WithDisposable(LitElement) {
return this.edgeless.service;
}
get gfx() {
return this.edgeless.gfx;
}
get edgelessTool() {
return this.edgeless.gfx.tool.currentToolOption$.peek();
}
@@ -162,7 +166,7 @@ export class EdgelessZoomToolbar extends WithDisposable(LitElement) {
.tooltip=${'Fit to screen'}
.tipPosition=${this._isVerticalBar() ? 'right' : 'top-end'}
.arrow=${!this._isVerticalBar()}
@click=${() => this.edgelessService.zoomToFit()}
@click=${() => this.gfx.fitToScreen()}
.iconContainerPadding=${4}
.disabled=${locked}
>