fix(gxnlpt): 收录/收藏切换时主滚动容器scrollTop跳变引起抖动
- 新增 _resetScrollToTopForViewSwitch: 在 contentView 切换前瞬时(behavior:'auto')把 .content-wrap 的 scrollTop 复位到 0 - openSubmitView/openFavoritesView 切换 contentView 之前调用 根因: list 视图主内容区高 ~3000px,submit/favorites 视图高 ~1200px。 用户从 list 视图某个分类(如"碳认证机构")点击收录/收藏时, 当前 scrollTop 通常 ~1500px,切换后 scrollTop 1500 > scrollHeight 1200, 浏览器自动把 scrollTop 钳到 0,造成"页面瞬间跳到顶部"的视觉抖动, sidebar sticky 周围容器/背景也被一起拉到顶部,共同表现为"侧边栏抖动"。 方案: contentView 切换前先瞬时复位 scrollTop,让浏览器只看到 1 次 layout, 避免 scrollTop 跳变。瞬时(behavior:'auto')而不是平滑(smooth), 因为平滑动画与 contentView 切换并行会多次回流反而更抖。 配合之前 deactived 钩子修复,本次一并提交。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
6b8e38e3d7
commit
21c894e025
@ -684,6 +684,21 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* gxnlpt 被 <keep-alive> 缓存,从本组件跳走时只会触发 deactivated,不会触发 destroyed。
|
||||
* 必须在此处清理 sidebar sticky:
|
||||
* 1. portal 到 body 下的 .gxnlpt-sidebar-sticky 元素如果不搬回 sidebar 容器,
|
||||
* 会继续以 position:fixed 钉在 viewport,叠加到其他路由页面顶部。
|
||||
* 2. inline 写入的 left/width/top 也需要清空,避免污染别的页面。
|
||||
* 否则用户跳转到首页等其它页面后,会在屏幕上看到 gxnlpt 的侧边栏残留,
|
||||
* 同时 devtools 中也能查到 element.style { left/width/top } 残留。
|
||||
*/
|
||||
deactivated() {
|
||||
this.unbindStackedNavMedia();
|
||||
this.clearScrollUnlock();
|
||||
this.destroyScrollSpy();
|
||||
this.destroySidebarSticky();
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.unbindStackedNavMedia();
|
||||
this.clearScrollUnlock();
|
||||
@ -1085,37 +1100,39 @@ export default {
|
||||
return;
|
||||
}
|
||||
this.destroyScrollSpy();
|
||||
// 视图切换前先把滚动容器重置到顶部(瞬时,无动画):
|
||||
// 滚动容器是 main.vue 的 .content-wrap,list 视图下高度 ~3000px,用户通常
|
||||
// 滚到某个分类(例如"碳认证机构",scrollTop 可能 ~1500px)。直接切到 submit
|
||||
// 视图后主内容区高度骤降到 ~1200px,scrollTop 1500 > scrollHeight 1200,
|
||||
// 浏览器会自动把 scrollTop 钳到 0,造成"页面瞬间跳到顶部"的视觉抖动。
|
||||
// 提前在切换前把 scrollTop 复位到 0,可避免这次不期望的 scrollTop 跳变。
|
||||
this._resetScrollToTopForViewSwitch();
|
||||
this.contentView = 'submit';
|
||||
this.resetSubmitForm();
|
||||
// 视图切换后 sidebar 容器高度可能变化,sticky 需要重新测量几何并刷新 left/width/top,
|
||||
// 否则 element.style 上残留旧值会让侧边栏在视口里"错位"。
|
||||
this.$nextTick(() => this.refreshSidebarStickyGeometry());
|
||||
},
|
||||
openFavoritesView() {
|
||||
if (!this.ensureLogin('查看我的收藏')) {
|
||||
return;
|
||||
}
|
||||
this.destroyScrollSpy();
|
||||
this._resetScrollToTopForViewSwitch();
|
||||
this.contentView = 'favorites';
|
||||
this.loadFavorites();
|
||||
// 视图切换后 sidebar 容器高度可能变化,sticky 需要重新测量几何并刷新 left/width/top,
|
||||
// 否则 element.style 上残留旧值会让侧边栏在视口里"错位"。
|
||||
this.$nextTick(() => this.refreshSidebarStickyGeometry());
|
||||
},
|
||||
/**
|
||||
* 视图切换后重新测量 sidebar / sticky 几何常量,并立即应用最新的 left/width/top。
|
||||
* 不重新走 initSidebarSticky/destroySidebarSticky(避免把 sticky 元素搬回容器再搬出,
|
||||
* 减少不必要的 DOM 抖动)。
|
||||
* 视图(list→submit/favorites 或反向)切换前,把滚动容器 .content-wrap 的
|
||||
* scrollTop 瞬时复位到 0,避免 contentView 切换导致主内容区 scrollHeight
|
||||
* 骤变、浏览器自动把 scrollTop 钳到 0 引发的"页面瞬间跳到顶部"视觉抖动。
|
||||
*
|
||||
* 用瞬时滚动(behavior: 'auto')是为了让 scrollTop 在浏览器进入下一次 layout
|
||||
* 之前就已经是 0,这样 contentView 切换时 scrollHeight 变化不会再次触发
|
||||
* scrollTop 钳位 —— 等于"用确定的 scrollTop 状态切换"替代"放任 scrollTop 跳变"。
|
||||
*/
|
||||
refreshSidebarStickyGeometry() {
|
||||
if (this.isStackedNavMode) return;
|
||||
this._recomputeStickyGeometry();
|
||||
// 清掉上次缓存的死区值,确保 _applySidebarSticky 一定会把新值写到 element.style
|
||||
this._lastStickyTop = undefined;
|
||||
this._lastStickyLeft = undefined;
|
||||
this._lastStickyWidth = undefined;
|
||||
this._lastStickyVisible = undefined;
|
||||
this._applySidebarSticky();
|
||||
_resetScrollToTopForViewSwitch() {
|
||||
const scrollRoot = this.getScrollRoot();
|
||||
if (!scrollRoot) return;
|
||||
if (scrollRoot.scrollTop === 0) return;
|
||||
scrollRoot.scrollTo({ top: 0, behavior: 'auto' });
|
||||
},
|
||||
async loadFavorites() {
|
||||
this.favoritesLoading = true;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user