From 1874a6e9ece37c12771a56c75f6e46ecdfcf9859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Fri, 2 Oct 2026 22:34:07 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B3=A8=E9=87=8A=EF=BC=9A=E4=B8=BA=E4=BC=9A?= =?UTF-8?q?=E8=AF=9D=E6=BB=9A=E5=8A=A8=E9=94=9A=E7=82=B9=E7=9A=84=E5=85=A8?= =?UTF-8?q?=E8=A1=A8=E9=87=8F=E6=B5=8B=E8=A1=A5=20TODO=20=E6=80=A7?= =?UTF-8?q?=E8=83=BD=E5=88=86=E6=9E=90=20-=20conversationScrollAnchor.ts?= =?UTF-8?q?=20=E7=9A=84=20readTopVisibleTurnAnchor=20=E5=BE=AA=E7=8E=AF?= =?UTF-8?q?=E4=B8=8A=E6=96=B9=E6=96=B0=E5=A2=9E=20//=20TODO(perf):=20?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=EF=BC=8C=E4=BB=85=E6=B3=A8=E9=87=8A=E3=80=81?= =?UTF-8?q?=E6=97=A0=E8=A1=8C=E4=B8=BA=E5=8F=98=E5=8C=96=20-=20=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E9=87=8F=E6=B5=8B=E5=8F=A3=E5=BE=84=EF=BC=9A=E6=9C=80?= =?UTF-8?q?=E5=9D=8F=20O(=E9=94=9A=E7=82=B9=E4=B9=8B=E5=89=8D=E7=9A=84?= =?UTF-8?q?=E5=9D=97=E6=95=B0)=20=E6=AC=A1=20getBoundingClientRect?= =?UTF-8?q?=EF=BC=8C=E5=90=8C=E5=B8=A7=E9=A6=96=E6=AC=A1=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E5=90=8E=E5=B8=83=E5=B1=80=E5=B7=B2=E5=B9=B2=E5=87=80=E3=80=81?= =?UTF-8?q?=E5=90=8E=E7=BB=AD=E4=B8=8D=E5=86=8D=E5=90=84=E8=87=AA=E8=A7=A6?= =?UTF-8?q?=E5=8F=91=20reflow=EF=BC=8C=E5=B1=9E=20low=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E6=98=AF=E6=8C=AF=E8=8D=A1=E6=BA=90=20-=20=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=20rAF=20=E8=8A=82=E6=B5=81=E4=B8=8E=E3=80=8C=E5=8F=AA=E5=9C=A8?= =?UTF-8?q?=E8=A1=A5=E5=81=BF=E5=89=8D=E6=89=8D=E8=AF=BB=E3=80=8D=E9=83=BD?= =?UTF-8?q?=E7=9C=81=E4=B8=8D=E6=8E=89=E8=BF=99=E6=AC=A1=E9=81=8D=E5=8E=86?= =?UTF-8?q?=EF=BC=8C=E6=95=85=E4=B8=8D=E9=87=87=E7=BA=B3=20-=20=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E4=BA=8C=E5=88=86=20/=20=E7=BB=AD=E6=89=AB=E8=A2=AB?= =?UTF-8?q?=E6=94=B6=E8=B5=B7=20
=20=E7=9A=84=200=20=E7=9F=A9?= =?UTF-8?q?=E5=BD=A2=E6=89=93=E6=96=AD=E5=8D=95=E8=B0=83=E6=80=A7=EF=BC=8C?= =?UTF-8?q?=E9=9C=80=E5=85=88=E5=AE=9A=E3=80=8C=E9=9A=90=E8=97=8F=E5=9D=97?= =?UTF-8?q?=E6=80=8E=E4=B9=88=E5=8F=82=E4=B8=8E=E3=80=8D=20-=20=E9=A6=96?= =?UTF-8?q?=E9=80=89=E5=90=8E=E7=BB=AD=E6=96=B9=E5=90=91=EF=BC=9A=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E4=B8=8A=E6=AC=A1=E5=91=BD=E4=B8=AD=E7=9A=84=E5=9D=97?= =?UTF-8?q?=E8=BA=AB=E4=BB=BD=E5=B9=B6=E4=BB=8E=E9=99=84=E8=BF=91=E7=BB=AD?= =?UTF-8?q?=E6=89=AB=EF=BC=8C=E4=B8=8D=E7=A0=B4=E5=9D=8F=E5=8D=95=E8=B0=83?= =?UTF-8?q?=E6=80=A7=E5=81=87=E8=AE=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Junie --- .../DirectProjectConversation/conversationScrollAnchor.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/ai-game-creator-shell/src/view/project-development/chat/components/DirectProjectConversation/conversationScrollAnchor.ts b/apps/ai-game-creator-shell/src/view/project-development/chat/components/DirectProjectConversation/conversationScrollAnchor.ts index 765104d5a..a16c65926 100644 --- a/apps/ai-game-creator-shell/src/view/project-development/chat/components/DirectProjectConversation/conversationScrollAnchor.ts +++ b/apps/ai-game-creator-shell/src/view/project-development/chat/components/DirectProjectConversation/conversationScrollAnchor.ts @@ -84,6 +84,14 @@ export function readTopVisibleTurnAnchor( ): ConversationTurnAnchor | null { const listTop = list.getBoundingClientRect().top; + // TODO(perf): 这个循环最坏是 O(锚点之前的块数) 次 `getBoundingClientRect()`,调用点是 `onScroll` + // (离底时)与每次 `compensateLayout`,超长历史里属于 low 级别的量测开销——同一帧里第一次调用 + // 之后布局已经干净,后续调用不会各自再触发一次 reflow,所以不是「每帧几百次强制布局」的振荡源。 + // 真要省掉遍历只能按「块在文档顺序里位置单调递增」做二分、或从上次命中处续扫;但被折进**收起** + // `
` 的块矩形全 0、同样在候选集合里,会打断这个单调性,二分前得先定「隐藏块怎么参与」, + // 而给锚点集合加「只收可见块」的规则又要每次子元素变化先量一遍,等于把省下的遍历又搬回来。 + // 若真机在超长历史里量到滚动手感问题,先走「缓存上次命中的块身份 + 从它附近续扫」这条不破坏 + // 单调性假设的路,而不是 rAF 节流(滚动事件本就按帧派发,同一帧去重收益极小)。 for (const block of collectTurnBlocks(list)) { const rect = block.getBoundingClientRect(); // 折进收起 `
` 的块在真实浏览器里没有布局盒(矩形全 0):它既不是用户正在读的