Skip to content

外观与动效 ​

这一页讲两件事:主题色与背景怎么算,动效怎么分级与驱动。

主题色 ​

品牌黄 #FFD322 是主色(悬浮 #FFBC00,按下 #E6A800,禁用 #99FFD322)。语义色另有成功 #3DDC84、危险 #F44336、警告 #B45309/#FBBF24,Nova 有自己一套紫/绿/琥珀,只在 Nova 区域出现。

它们定义在 App.xaml 的 ThemeDictionaries 里,覆盖 WinUI 的 SystemAccentColorLight1..Dark3 与 AccentFillColorDefault..Disabled——也就是说改这一处,整个应用的强调色跟着变,不必逐个控件改。

一条容易忽略的规则:黄色面上必须是黑字。品牌黄亮度高,白字对比度只有约 1.4:1,所以 accent 背景上的文字都用 #1A1A1A 系;浅色主题上「黄色文字」反过来要用 #B38500。

主题切换本身只是给根元素设 RequestedTheme:

csharp
public void ApplyTheme(string theme, FrameworkElement? root)
{
    root.RequestedTheme = theme switch
    {
        "light" => ElementTheme.Light,
        "dark"  => ElementTheme.Dark,
        _       => ElementTheme.Default,   // system
    };
}

动效四档与四道闸 ​

设置里的「界面动效」是四档,键与 ui_motion_level 一一对应:minimal(极少,只有页面切换转场)、lite(较少,+跟手光斑、毛玻璃卡片、悬停/入场微反馈)、balanced(均衡,+全局实时鼠标光照、阴影跟随鼠标、边缘引力形变,默认)、full(全部,+弹簧形变、窗口环境光斑)。 MotionService 把四档展开成四道闸:

csharp
public bool Enabled  => Level >= UiMotionLevel.Lite && SystemAnimationsEnabled;  // 合成动效
public bool Transitions => SystemAnimationsEnabled;                              // 页面转场
public bool LightFx  => Level >= UiMotionLevel.Balanced && SystemAnimationsEnabled;
public bool FullFx   => Level == UiMotionLevel.Full && SystemAnimationsEnabled;

注意 Transitions 那一条:页面切换转场不受档位限制。最少档也保留转场,因为那正是这一档的定义(「只有最基本的页面切换动画」)。它只受系统动画开关约束。

「系统关闭动画时全部动效关闭」是硬原则,读的是 UISettings().AnimationsEnabled。探测失败按有动效处理:

csharp
try { return new Windows.UI.ViewManagement.UISettings().AnimationsEnabled; }
catch { return true; }

理由是语义不对称的——用户显式关掉动画是明确表态,我们应当尊重;而探测失败只是我们不知道,用户并没有表达任何意愿。把它当成「关」就是用一次技术故障剥夺了用户的动效。

节奏与曲线 ​

时长和曲线集中在 MotionFx。全局节奏系数 Pace = 1.5——刻意比 Fluent 标准档从容一档,只放大时长、不改曲线形状:

csharp
public const int EnterMs = (int)(250 * Pace);        // 375ms 进场
public const int ExitMs  = (int)(140 * Pace);        // 210ms 退场
public const int HoverFadeInMs  = (int)(200 * Pace); // 300ms
public const int HoverFadeOutMs = (int)(300 * Pace); // 450ms(比淡入长,留尾迹)

曲线有四套:进场 Fast Out Slow In (0.1,0.9)/(0.2,1)、退场 Slow Out Fast In (0.7,0)/(1,0.5)、控件 (0.16,1)/(0.3,1)、纯明暗交叉淡入淡出 (0.33,0)/(0.67,1)。最后这套单列出来有原因:Fluent 的进场样条「起步极快」,用在阴影、高亮这类纯明暗渐变上会退化成「啪一下没了」——这正是悬停阴影显得生硬的原因。所以内容进出场用 Enter/Exit,阴影和光斑一律用匀称的 Fade。

毛玻璃强度:一个滑块喂两层 ​

「毛玻璃强度」这一个值同时喂给窗口亚克力的 Tint 和壁纸遮罩的不透明度。两层都吃这个值,是为了让「背景 = 关闭(纯毛玻璃)」时拖滑块也有可见变化——此前只喂遮罩,而遮罩在没有壁纸时整层是隐藏的,那个设置看上去完全没作用。跟随主题时用默认配方(浅 0.25 / 深 0.45)。

模糊作用在图片表面,不是 backdrop ​

窗口背景是三层:亚克力 backdrop → WindowBackgroundHost 的三层 → 常规 XAML 内容。模糊用 composition 的 GaussianBlurEffect(BlurAmount = 24),但作用在图片表面上。

模糊 backdrop 会和背后的清晰壁纸重影,于是「背景不透明度 50%」这句话说不清指的是哪一层。作用在图片表面上,遮罩层的不透明度就干净地只管「盖多少」。组合器不可用时静默退化为清晰背景图。

极少档会把毛玻璃退化成实色卡

MotionMaterials.Apply 在极少档把 GlassCardBrush 的 TintOpacity 拉到 1.0——亚克力完全被 Tint 覆盖,模糊采样不再可见。极少档的定位是「只保留最基本的页面切换动画」,毛玻璃卡片属于「较少」档才启用的能力,所以它也被收走了。原配方记在 Recipe 里(浅 0.72 / 深 0.75),其余档照此恢复。

相关链接:窗口与导航 · 自绘控件与交互行为

基于 MIT 许可证(有修改)开源