Skip to content

feat(editor): remember scroll position per scene across navigation - #57

Merged
shanchuann merged 1 commit into
mainfrom
feat/editor-scene-scroll-memory
Jun 7, 2026
Merged

shanchuann merged 1 commit into
mainfrom
feat/editor-scene-scroll-memory

Conversation

@shanchuann

Copy link
Copy Markdown
Owner

功能描述

切换场景(底部"上一个/下一个"或左侧场景列表)时:

  • 第一次进入某个场景 → 滚到顶部
  • 再次回到曾经看过的场景 → 恢复上次离开时的滚动位置

之前的行为是 `scrollTop` 不动,浏览器把上一个场景的滚动值带到新场景里,半截显示新场景中部;从场景 5 回到场景 3 时之前看到哪里完全丢失。

实现思路

两个 `useRef`:

  • `scrollContainerRef` 指向 `flex-1 overflow-auto` 的主滚动容器
  • `sceneScrollPositionsRef` 是 `Map<sceneId, scrollTop>`,放 ref 不放 state,因为它会在每次滚动 tick 上更新,state 会把整个编辑器 re-render 一次/滚动事件

两个 `useEffect`:

  1. 监听当前场景的 `scroll` 事件,写入 map(passive listener)。`activeSceneId` 变化时清理 + 重挂,确保不会把新场景的滚动写到旧 key 上
  2. `activeSceneId` 变化时读取 map,`container.scrollTop = saved ?? 0`

React commit 顺序保证:DOM 提交 → cleanup 旧 listener → 第二个 effect 执行设 scrollTop → 第一个 effect 挂新 listener。restore 时触发的 scroll 事件落在没监听器的窗口里,不会污染 map。

Map 挂在 ref instance 上,作用域是 Editor 路由实例;上传新文件会让 Editor 路由 unmount 重新 mount,map 自动重置,无需手动清空。

测试方式

  • `web/` `npx tsc -b` ✅
  • `eslint`: 1 条 main 已有的 `set-state-in-effect` 历史告警,0 新增
  • 人眼验证:
    1. 进入 `/editor` 任一节拍多的场景
    2. 滚到中段
    3. 用底部 `下一个场景` 切换 → 新场景应该回到顶部
    4. 回到原场景 → 应该恢复到刚才中段位置

依赖与复用声明

无新增依赖。复用现有 `flex-1 overflow-auto` 主滚动容器;只新增一个 ref。

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

Switching scenes via the bottom prev/next links reset scrollTop to
the cached value the browser happened to have — often partway down
the previous scene's content, occasionally at 0 if the new content
was shorter. Going back to an earlier scene lost any in-progress
position; if the author was reading scene 3, jumped to scene 5 to
fix a beat, and came back, scene 3 reappeared at the top.

A Map<sceneId, scrollTop> on a ref captures the offset on every
scroll tick (passive listener, no re-render). When activeSceneId
changes, a second effect reads the saved value and sets scrollTop
— or 0 if this is the scene's first visit. The browser clamps an
oversized saved value automatically when the new scene's content
height is smaller.

The map lives on a ref scoped to the route instance; a new upload
remounts the route, so positions reset without manual cleanup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shanchuann
shanchuann merged commit a990146 into main Jun 7, 2026
2 checks passed
@shanchuann
shanchuann deleted the feat/editor-scene-scroll-memory branch June 7, 2026 08:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant