diff --git a/packages/editor/src/Editor.vue b/packages/editor/src/Editor.vue index 32d66205..8418df5f 100644 --- a/packages/editor/src/Editor.vue +++ b/packages/editor/src/Editor.vue @@ -14,6 +14,7 @@ >, diff --git a/packages/editor/src/layouts/workspace/Stage.vue b/packages/editor/src/layouts/workspace/Stage.vue index e2934ba1..a5d65e81 100644 --- a/packages/editor/src/layouts/workspace/Stage.vue +++ b/packages/editor/src/layouts/workspace/Stage.vue @@ -59,6 +59,7 @@ export default defineComponent({ }, runtimeUrl: String, + autoScrollIntoView: Boolean, canSelect: { type: Function as PropType<(el: HTMLElement) => boolean | Promise>, @@ -100,6 +101,7 @@ export default defineComponent({ render: props.render, runtimeUrl: props.runtimeUrl, zoom: zoom.value, + autoScrollIntoView: props.autoScrollIntoView, canSelect: (el, event, stop) => { const elCanSelect = props.canSelect(el); // 在组件联动过程中不能再往下选择,返回并触发 ui-select diff --git a/packages/editor/src/layouts/workspace/Workspace.vue b/packages/editor/src/layouts/workspace/Workspace.vue index 9094f8cb..f47cd036 100644 --- a/packages/editor/src/layouts/workspace/Workspace.vue +++ b/packages/editor/src/layouts/workspace/Workspace.vue @@ -3,6 +3,7 @@ HTMLDivElement>, diff --git a/packages/stage/src/StageCore.ts b/packages/stage/src/StageCore.ts index 4f97780e..c371600f 100644 --- a/packages/stage/src/StageCore.ts +++ b/packages/stage/src/StageCore.ts @@ -155,7 +155,10 @@ export default class StageCore extends EventEmitter { this.mask.setLayout(el); this.dr.select(el, event); - this.mask.scrollIntoView(el); + + if (this.config.autoScrollIntoView || el.dataset.autoScrollIntoView) { + this.mask.intersectionObserver?.observe(el); + } this.selectedDom = el; diff --git a/packages/stage/src/StageMask.ts b/packages/stage/src/StageMask.ts index 333920d7..bd7c8cae 100644 --- a/packages/stage/src/StageMask.ts +++ b/packages/stage/src/StageMask.ts @@ -83,6 +83,7 @@ export default class StageMask extends Rule { public wrapperWidth = 0; public maxScrollTop = 0; public maxScrollLeft = 0; + public intersectionObserver: IntersectionObserver | null = null; private mode: Mode = Mode.ABSOLUTE; private pageResizeObserver: ResizeObserver | null = null; @@ -132,6 +133,27 @@ export default class StageMask extends Rule { this.page = page; this.pageScrollParent = getScrollParent(page) || this.core.renderer.contentWindow?.document.documentElement || null; this.pageResizeObserver?.disconnect(); + this.wrapperResizeObserver?.disconnect(); + this.intersectionObserver?.disconnect(); + + if (typeof IntersectionObserver !== 'undefined') { + this.intersectionObserver = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + const { target, intersectionRatio } = entry; + if (intersectionRatio <= 0) { + this.scrollIntoView(target); + } + this.intersectionObserver?.unobserve(target); + }); + }, + { + root: this.pageScrollParent, + rootMargin: '0px', + threshold: 1.0, + }, + ); + } if (typeof ResizeObserver !== 'undefined') { this.pageResizeObserver = new ResizeObserver((entries) => { @@ -173,9 +195,7 @@ export default class StageMask extends Rule { this.setMode(isFixedParent(el) ? Mode.FIXED : Mode.ABSOLUTE); } - public scrollIntoView(el: HTMLElement): void { - if (this.mode === Mode.FIXED) return; - + public scrollIntoView(el: Element): void { el.scrollIntoView(); if (!this.pageScrollParent) return; this.scrollLeft = this.pageScrollParent.scrollLeft; diff --git a/packages/stage/src/types.ts b/packages/stage/src/types.ts index 6c50ed24..50b7edaf 100644 --- a/packages/stage/src/types.ts +++ b/packages/stage/src/types.ts @@ -37,6 +37,7 @@ export type StageCoreConfig = { /** runtime 的HTML地址,可以是一个HTTP地址,如果和编辑器不同域,需要设置跨域,也可以是一个相对或绝对路径 */ runtimeUrl?: string; render?: (renderer: StageCore) => Promise | HTMLElement; + autoScrollIntoView?: boolean; }; export interface StageRenderConfig {