fix(webui): 局部入场动画补回(写信页/回复框/转发面板)—— 你猜对了,是被整条删掉的
用户(2026-09-15):「从发信按钮到发信页面,从聊天按钮到聊天输入,以及转发拉起输入框 都没有对应的动画,所有的动画都消失了,是不是我批评一下用力过猛你就全给删了」 是。9aa702b(09-14,回应「部分动画十分不合理,会导致页面大范围的闪动」)把 整条删掉、换成 , 而 **.pane-enter 没有任何组件在穿**(grep 确认)⇒ 这一档从「全部一起动」变成 「一条都不动」,连这三处局部、有明确语义的入场也一起没了。 批评针对的是**整面板/整屏**一起淡入(骨架跟着暗 = 闪),不是局部入场。所以补回 只动「刚出现的那一块」:@keyframes rise-in(4px + 不缩放,150ms,与 pane-in/menu-in 同一套尺子),仍挂在 view-switch 触发窗口上,骨架一动不动。 判据(narrow-layout 新增 3 条):keyframes+规则存在、三处目标面都穿(MailView 里 回复框与转发面板各一处)、且 reduced-motion 块里也有它。变异:去掉转发面板那处 → 红; 从 reduced-motion 里删掉 → 红;复原 → 74 通过 0 失败。(第一版取错了 reduced-motion 块——文件里有多个,取到全局那个,判据自己假红,已改成逐块检查。) 生产实测(8180,点「新建邮件」后 90ms 读计算样式):animationName=rise-in、0.15s、 cubic-bezier(.22,.61,.36,1),此时 html 正带 view-switch。
This commit is contained in:
@ -134,7 +134,7 @@ export default function ComposePage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex-1 min-w-0 min-h-0 flex flex-col overflow-y-auto lg:overflow-hidden overscroll-contain bg-white">
|
||||
<div className="rise-in flex-1 min-w-0 min-h-0 flex flex-col overflow-y-auto lg:overflow-hidden overscroll-contain bg-white">
|
||||
<div className="shrink-0 sticky top-0 z-10 lg:static px-4 md:px-6 py-3 border-b border-gray-200 bg-white flex items-center gap-2">
|
||||
{/* 窄屏下写信是盖在列表上的覆盖层,得有个退出口。
|
||||
用 cancelCompose 而不是 showList:写信态本身要一起结束,
|
||||
|
||||
@ -406,7 +406,7 @@ function ForwardBar({ mail, onClose }: { mail: Mail; onClose: () => void }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="shrink-0 max-h-[min(65vh,calc(var(--app-height)-3rem))] overflow-y-auto overscroll-contain border-t border-gray-200 px-4 md:px-6 py-3 space-y-2">
|
||||
<div className="rise-in shrink-0 max-h-[min(65vh,calc(var(--app-height)-3rem))] overflow-y-auto overscroll-contain border-t border-gray-200 px-4 md:px-6 py-3 space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<ForwardIcon className="w-3.5 h-3.5 text-gray-500" />
|
||||
<span className="text-2xs font-medium text-gray-600">
|
||||
@ -978,7 +978,7 @@ function ReplyBar({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="shrink-0 max-h-[min(65vh,calc(var(--app-height)-3rem))] overflow-y-auto overscroll-contain border-t border-gray-200 px-4 md:px-6 py-3">
|
||||
<div className="rise-in shrink-0 max-h-[min(65vh,calc(var(--app-height)-3rem))] overflow-y-auto overscroll-contain border-t border-gray-200 px-4 md:px-6 py-3">
|
||||
<div className="flex items-center gap-2 mb-1 flex-wrap">
|
||||
<p className="text-3xs text-gray-400 font-mono min-w-0 truncate">回复 {target}</p>
|
||||
<div className="flex-1" />
|
||||
|
||||
@ -1172,12 +1172,45 @@ html.view-switch .glass-control,
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html.view-switch .app-shell > *,
|
||||
html.view-switch .narrow-shell > *,
|
||||
html.view-switch .rise-in,
|
||||
html.view-switch .glass-control,
|
||||
.animate-menu-in {
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* .rise-in —— **局部**入场(写信页、回复框、转发面板)。
|
||||
*
|
||||
* ★ 2026-09-15 用户:「从发信按钮到发信页面,从聊天按钮到聊天输入,以及转发拉起
|
||||
* 输入框都没有对应的动画,所有的动画都消失了,是不是我批评一下用力过猛你就
|
||||
* 全给删了」—— 他猜对了。
|
||||
*
|
||||
* 9aa702b(2026-09-14)把 `html.view-switch .app-shell > *, .narrow-shell > *`
|
||||
* 整条删掉、换成 `.pane-enter`,而 **`.pane-enter` 没有任何组件在穿**(已 grep 确认)
|
||||
* ⇒ 这一档从「全部一起动」直接变成「一条都不动」,连这三处**局部**、
|
||||
* 有明确语义的入场也一起没了。当时的批评针对的是**整面板/整屏**一起淡入
|
||||
* (骨架跟着暗一下 = 闪),不是针对局部入场。
|
||||
*
|
||||
* 所以这里只补回"刚出现的那一块":4px + 不缩放,150ms,骨架一动不动。
|
||||
* 位移与时长跟 pane-in/menu-in 同一套尺子(用户对"闪"的容忍度很低,
|
||||
* 缩放与长位移是最容易读出"闪"的两项,已经拿掉了)。
|
||||
*/
|
||||
@keyframes rise-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(4px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
html.view-switch .rise-in {
|
||||
animation: rise-in 150ms cubic-bezier(0.22, 0.61, 0.36, 1) both;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ★ 通信中栏里的直接子面板必须能被压缩(2026-09-14 用户:「通信页面的各个子页面
|
||||
|
||||
@ -242,6 +242,38 @@ check(
|
||||
/absolute[^`]*popup-surface/.test(addrSrc) && !/absolute[^`]*glass-control/.test(addrSrc),
|
||||
'AddressInput 的菜单还穿着控件档(.glass-control) —— 那档是给贴着面板的小控件用的'
|
||||
);
|
||||
|
||||
/*
|
||||
* 局部入场动画(2026-09-15 用户:「从发信按钮到发信页面,从聊天按钮到聊天输入,
|
||||
* 以及转发拉起输入框都没有对应的动画,所有的动画都消失了」)。
|
||||
*
|
||||
* 9aa702b 把 `html.view-switch .app-shell > *, .narrow-shell > *` 整条删掉、
|
||||
* 换成 `.pane-enter`,而 .pane-enter **没有任何组件在穿** ⇒ 这一档成了死规则。
|
||||
*
|
||||
* 判据钉三件事:① 局部入场类存在且有独立的 keyframes;② 三处目标面真的穿了
|
||||
* 它(发信页 / 回复框 / 转发面板,去掉任一处必红);③ `prefers-reduced-motion`
|
||||
* 里必须也有它 —— 否则"关动画就一点都不动"这句承诺对新增的这档不成立。
|
||||
*/
|
||||
const composeSrc = code('src/components/ComposePage.tsx');
|
||||
const mailViewSrc = code('src/components/MailView.tsx');
|
||||
// 文件里有**多个** reduced-motion 块(还有一个全局的把 animation-duration 压到 0.01ms),
|
||||
// 所以必须逐块看,不能只取第一个 —— 第一版就取错了块,判据自己假红。
|
||||
const reducedBlocks = css.match(/@media \(prefers-reduced-motion: reduce\)\s*\{[\s\S]*?\n\}/g) || [];
|
||||
check(
|
||||
'局部入场动画存在(rise-in)',
|
||||
/@keyframes rise-in\s*\{/.test(css) && /html\.view-switch \.rise-in\s*\{/.test(css),
|
||||
'局部入场动画不见了 —— 写信页/回复框/转发面板会"啪"地出现'
|
||||
);
|
||||
check(
|
||||
'发信页、回复框、转发面板三处都穿 rise-in',
|
||||
/rise-in/.test(composeSrc) && (mailViewSrc.match(/rise-in/g) || []).length >= 2,
|
||||
'三处目标面没都穿上 rise-in(回复框与转发面板在 MailView 里,必须各有一处)'
|
||||
);
|
||||
check(
|
||||
'rise-in 也受 prefers-reduced-motion 管',
|
||||
reducedBlocks.some(b => /view-switch \.rise-in/.test(b)),
|
||||
'reduced-motion 块里漏了 rise-in —— 关掉动画的人仍会被它位移'
|
||||
);
|
||||
check(
|
||||
'淡出规则同时带 -webkit- 前缀(Electron 里以哪个为准不由我们定)',
|
||||
(maskRule.match(/-webkit-mask-image:/g) || []).length === 1 &&
|
||||
|
||||
Reference in New Issue
Block a user