mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2026-08-07 13:29:21 +00:00
fix(stage): 修复切换页面后 StageMask 尺寸与滚动偏移异常
pageResize 仅同步当前页且已挂载节点,忽略 0 尺寸回调;observe 时立即同步并在 destroy 时断开 intersectionObserver。
This commit is contained in:
parent
d5a2e25e18
commit
a9cda719bc
@ -117,19 +117,27 @@ export default class StageMask extends Rule {
|
||||
this.page = page;
|
||||
this.initObserverIntersection();
|
||||
this.initObserverWrapper();
|
||||
// 页面切换后立即同步一次;ResizeObserver 首次回调可能仍上报旧页/0 尺寸
|
||||
this.syncPageSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理页面大小变更,同步页面和mask大小
|
||||
* 处理页面大小变更,同步页面和 mask 大小
|
||||
* @description 仅处理当前 page 且仍挂载在文档中的 entry;非当前页、已卸载或宽高为 0 的回调会被忽略
|
||||
* @param entries ResizeObserverEntry,获取页面最新大小
|
||||
*/
|
||||
public pageResize(entries: ResizeObserverEntry[]): void {
|
||||
const [entry] = entries;
|
||||
const { clientHeight, clientWidth } = entry.target;
|
||||
this.setHeight(clientHeight);
|
||||
this.setWidth(clientWidth);
|
||||
if (!this.page || !entries.length) return;
|
||||
|
||||
this.scroll();
|
||||
// 取当前 page 对应 entry,避免 batch 中首项是旧页时整批被丢弃
|
||||
const entry = entries.find((item) => item.target === this.page);
|
||||
if (!entry) return;
|
||||
|
||||
const { target } = entry;
|
||||
// 旧页卸载后仍可能回调,已断开连接的节点不再同步尺寸
|
||||
if (!target.isConnected) return;
|
||||
|
||||
this.applyPageSize(target.clientWidth, target.clientHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -186,6 +194,7 @@ export default class StageMask extends Rule {
|
||||
this.content?.remove();
|
||||
this.page = null;
|
||||
this.pageScrollParent = null;
|
||||
this.intersectionObserver?.disconnect();
|
||||
this.wrapperResizeObserver?.disconnect();
|
||||
}
|
||||
|
||||
@ -287,6 +296,26 @@ export default class StageMask extends Rule {
|
||||
this.content.dispatchEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从当前页面同步蒙层宽高,并修正滚动偏移
|
||||
*/
|
||||
private syncPageSize(): void {
|
||||
if (!this.page?.isConnected) return;
|
||||
|
||||
this.applyPageSize(this.page.clientWidth, this.page.clientHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用页面尺寸到蒙层;宽高为 0 时忽略,避免切换/布局过程中把 editor-mask 置空
|
||||
*/
|
||||
private applyPageSize(clientWidth: number, clientHeight: number): void {
|
||||
if (clientWidth <= 0 || clientHeight <= 0) return;
|
||||
|
||||
this.setHeight(clientHeight);
|
||||
this.setWidth(clientWidth);
|
||||
this.scroll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置蒙层高度
|
||||
* @param height 高度
|
||||
|
||||
@ -79,11 +79,124 @@ describe('StageMask', () => {
|
||||
const page = globalThis.document.createElement('div');
|
||||
Object.defineProperty(page, 'clientWidth', { value: 400, configurable: true });
|
||||
Object.defineProperty(page, 'clientHeight', { value: 300, configurable: true });
|
||||
globalThis.document.body.appendChild(page);
|
||||
mask.observe(page);
|
||||
|
||||
mask.pageResize([makeResizeEntry(page)]);
|
||||
expect(mask.width).toBe(400);
|
||||
expect(mask.height).toBe(300);
|
||||
expect(mask.content.style.width).toBe('400px');
|
||||
expect(mask.content.style.height).toBe('300px');
|
||||
});
|
||||
|
||||
test('observe 立即同步尺寸,并从长页切到短页时修正滚动偏移', () => {
|
||||
mask = new StageMask({ disabledRule: true });
|
||||
const tallPage = globalThis.document.createElement('div');
|
||||
Object.defineProperty(tallPage, 'clientWidth', { value: 400, configurable: true });
|
||||
Object.defineProperty(tallPage, 'clientHeight', { value: 1000, configurable: true });
|
||||
globalThis.document.body.appendChild(tallPage);
|
||||
mask.observe(tallPage);
|
||||
mask.wrapperWidth = 400;
|
||||
mask.wrapperHeight = 300;
|
||||
(mask as any).setMaxScrollLeft();
|
||||
(mask as any).setMaxScrollTop();
|
||||
mask.scrollTop = 500;
|
||||
(mask as any).scroll();
|
||||
expect(mask.scrollTop).toBe(500);
|
||||
|
||||
const shortPage = globalThis.document.createElement('div');
|
||||
Object.defineProperty(shortPage, 'clientWidth', { value: 400, configurable: true });
|
||||
Object.defineProperty(shortPage, 'clientHeight', { value: 400, configurable: true });
|
||||
globalThis.document.body.appendChild(shortPage);
|
||||
mask.observe(shortPage);
|
||||
|
||||
expect(mask.width).toBe(400);
|
||||
expect(mask.height).toBe(400);
|
||||
expect(mask.maxScrollTop).toBe(100);
|
||||
expect(mask.scrollTop).toBe(100);
|
||||
expect(mask.content.style.transform).toBe('translate3d(0px, -100px, 0)');
|
||||
});
|
||||
|
||||
test('pageResize 忽略非当前页、已卸载页与 0 尺寸,避免切换页面后 editor-mask 变为 0', () => {
|
||||
mask = new StageMask({ disabledRule: true });
|
||||
const oldPage = globalThis.document.createElement('div');
|
||||
Object.defineProperty(oldPage, 'clientWidth', { value: 400, configurable: true });
|
||||
Object.defineProperty(oldPage, 'clientHeight', { value: 300, configurable: true });
|
||||
globalThis.document.body.appendChild(oldPage);
|
||||
mask.observe(oldPage);
|
||||
expect(mask.width).toBe(400);
|
||||
expect(mask.height).toBe(300);
|
||||
|
||||
const newPage = globalThis.document.createElement('div');
|
||||
Object.defineProperty(newPage, 'clientWidth', { value: 500, configurable: true });
|
||||
Object.defineProperty(newPage, 'clientHeight', { value: 600, configurable: true });
|
||||
globalThis.document.body.appendChild(newPage);
|
||||
mask.observe(newPage);
|
||||
expect(mask.width).toBe(500);
|
||||
expect(mask.height).toBe(600);
|
||||
expect(mask.content.style.width).toBe('500px');
|
||||
expect(mask.content.style.height).toBe('600px');
|
||||
|
||||
// 仍连接但不是当前 page:忽略
|
||||
Object.defineProperty(oldPage, 'clientWidth', { value: 100, configurable: true });
|
||||
Object.defineProperty(oldPage, 'clientHeight', { value: 100, configurable: true });
|
||||
mask.pageResize([makeResizeEntry(oldPage)]);
|
||||
expect(mask.width).toBe(500);
|
||||
expect(mask.height).toBe(600);
|
||||
|
||||
// batch 首项是旧页、后续是当前页:仍应应用当前页尺寸
|
||||
Object.defineProperty(newPage, 'clientWidth', { value: 520, configurable: true });
|
||||
Object.defineProperty(newPage, 'clientHeight', { value: 620, configurable: true });
|
||||
mask.pageResize([makeResizeEntry(oldPage), makeResizeEntry(newPage)]);
|
||||
expect(mask.width).toBe(520);
|
||||
expect(mask.height).toBe(620);
|
||||
|
||||
// 旧页面卸载后 ResizeObserver 可能仍回调,不应覆盖当前蒙层尺寸
|
||||
Object.defineProperty(oldPage, 'clientWidth', { value: 0, configurable: true });
|
||||
Object.defineProperty(oldPage, 'clientHeight', { value: 0, configurable: true });
|
||||
oldPage.remove();
|
||||
mask.pageResize([makeResizeEntry(oldPage)]);
|
||||
expect(mask.width).toBe(520);
|
||||
expect(mask.height).toBe(620);
|
||||
|
||||
// 当前 page 已断开连接:忽略
|
||||
newPage.remove();
|
||||
Object.defineProperty(newPage, 'clientWidth', { value: 100, configurable: true });
|
||||
Object.defineProperty(newPage, 'clientHeight', { value: 100, configurable: true });
|
||||
mask.pageResize([makeResizeEntry(newPage)]);
|
||||
expect(mask.width).toBe(520);
|
||||
expect(mask.height).toBe(620);
|
||||
expect(mask.content.style.width).toBe('520px');
|
||||
expect(mask.content.style.height).toBe('620px');
|
||||
|
||||
// 重新挂载后短暂为 0 时也应忽略
|
||||
globalThis.document.body.appendChild(newPage);
|
||||
Object.defineProperty(newPage, 'clientWidth', { value: 0, configurable: true });
|
||||
Object.defineProperty(newPage, 'clientHeight', { value: 0, configurable: true });
|
||||
mask.pageResize([makeResizeEntry(newPage)]);
|
||||
expect(mask.width).toBe(520);
|
||||
expect(mask.height).toBe(620);
|
||||
expect(mask.content.style.width).toBe('520px');
|
||||
expect(mask.content.style.height).toBe('620px');
|
||||
});
|
||||
|
||||
test('observe 时 page 宽高为 0 不更新蒙层尺寸', () => {
|
||||
mask = new StageMask({ disabledRule: true });
|
||||
const firstPage = globalThis.document.createElement('div');
|
||||
Object.defineProperty(firstPage, 'clientWidth', { value: 400, configurable: true });
|
||||
Object.defineProperty(firstPage, 'clientHeight', { value: 300, configurable: true });
|
||||
globalThis.document.body.appendChild(firstPage);
|
||||
mask.observe(firstPage);
|
||||
|
||||
const emptyPage = globalThis.document.createElement('div');
|
||||
Object.defineProperty(emptyPage, 'clientWidth', { value: 0, configurable: true });
|
||||
Object.defineProperty(emptyPage, 'clientHeight', { value: 0, configurable: true });
|
||||
globalThis.document.body.appendChild(emptyPage);
|
||||
mask.observe(emptyPage);
|
||||
|
||||
expect(mask.page).toBe(emptyPage);
|
||||
expect(mask.width).toBe(400);
|
||||
expect(mask.height).toBe(300);
|
||||
});
|
||||
|
||||
test('wheel 事件更新 scroll 并 emit scroll', () => {
|
||||
@ -91,6 +204,7 @@ describe('StageMask', () => {
|
||||
const page = globalThis.document.createElement('div');
|
||||
Object.defineProperty(page, 'clientWidth', { value: 1000, configurable: true });
|
||||
Object.defineProperty(page, 'clientHeight', { value: 1000, configurable: true });
|
||||
globalThis.document.body.appendChild(page);
|
||||
mask.observe(page);
|
||||
mask.pageResize([makeResizeEntry(page)]);
|
||||
mask.wrapperWidth = 400;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user