From 58d317afce71d415ce49131ce443b93d91b1fad1 Mon Sep 17 00:00:00 2001 From: Jz0ojiang Date: Tue, 11 Aug 2026 17:04:11 +0800 Subject: [PATCH] =?UTF-8?q?=E9=93=BE=E5=BC=8F=E4=BB=A3=E7=90=86=E6=AE=B5?= =?UTF-8?q?=E5=87=BA=E7=8E=B0=E6=B6=88=E5=A4=B1=E4=B8=8D=E5=86=8D=E6=8A=96?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 抖动有两个来源。主要的是 .block + .block 给的 24px 上外边距在高度折叠 动画之外,出现和消失的瞬间各跳一下,折叠钩子现在把 margin-top 和高度 一起做插值。次要的是整页高度跨过视口阈值时滚动条出现消失让内容横移, 用 scrollbar-gutter 把滚动条位置常驻留出来。 --- web/styles/tokens.css | 5 +++++ web/views/InboundDetail.vue | 15 +++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/web/styles/tokens.css b/web/styles/tokens.css index ea89a2f..e0b4b0f 100644 --- a/web/styles/tokens.css +++ b/web/styles/tokens.css @@ -112,6 +112,11 @@ body, height: 100%; } +/* 页面高度跨过视口阈值时滚动条出现消失,内容会横向抖一下;把滚动条的位置常驻留出来 */ +html { + scrollbar-gutter: stable; +} + body { margin: 0; background: var(--c-bg); diff --git a/web/views/InboundDetail.vue b/web/views/InboundDetail.vue index fefd377..6d92ef5 100644 --- a/web/views/InboundDetail.vue +++ b/web/views/InboundDetail.vue @@ -158,23 +158,29 @@ function lockHeight() { } /** - * v-if 内容的展开收起动画:高度在 0 和实际值之间过渡,结束后清掉内联样式。 - * 给配置表单和链式代理段用。auto 高度不可插值,只能量出来做。 + * v-if 内容的展开收起动画:高度和上外边距一起在 0 和实际值之间过渡,结束后清掉 + * 内联样式。给配置表单和链式代理段用。auto 高度不可插值,只能量出来做。 + * 外边距必须一起动:链式代理段顶着 .block + .block 给的 24px margin,它在高度 + * 动画之外,不带上它出现和消失的瞬间就各抖一下。 */ function foldEnter(element: Element, done: () => void) { const el = element as HTMLElement // 先量再压:此刻元素是自然高度,revealBottom 量到的就是折叠结束后的位置 revealBottom() + const margin = getComputedStyle(el).marginTop el.style.height = '0' + el.style.marginTop = '0' el.style.overflow = 'hidden' void el.offsetHeight - el.style.transition = 'height 0.15s ease' + el.style.transition = 'height 0.15s ease, margin-top 0.15s ease' el.style.height = `${String(el.scrollHeight)}px` + el.style.marginTop = margin el.addEventListener( 'transitionend', () => { el.style.transition = '' el.style.height = '' + el.style.marginTop = '' el.style.overflow = '' done() }, @@ -187,8 +193,9 @@ function foldLeave(element: Element, done: () => void) { el.style.height = `${String(el.offsetHeight)}px` el.style.overflow = 'hidden' void el.offsetHeight - el.style.transition = 'height 0.15s ease' + el.style.transition = 'height 0.15s ease, margin-top 0.15s ease' el.style.height = '0' + el.style.marginTop = '0' el.addEventListener('transitionend', done, { once: true }) }