当前位置: 首页 > news >正文

流畅设计 Fluent Design System 中的光照效果 RevealBrush,WPF 也能模拟实现啦!

UWP 才能使用的流畅设计效果好惊艳,写新的 UWP 程序可以做出更漂亮的 UI 啦!然而古老的 WPF 项目也想解解馋怎么办?

于是我动手实现了一个!


迫不及待看效果

光照效果
▲ 是不是很像 UWP 中的 RevealBorderBrush

不止是效果像,连 XAML 写法也像:

<Border BorderThickness="1" Margin="50,34,526,348">
    <Border.BorderBrush>
        <demo:RevealBorderBrush />
    </Border.BorderBrush>
</Border>
<Border BorderThickness="1" Margin="50,76,526,306">
    <Border.BorderBrush>
        <demo:RevealBorderBrush Color="White" FallbackColor="Gray" />
    </Border.BorderBrush>
</Border>

▲ 模拟得很像的 RevealBorderBrush 的 XAML 写法

当然,窗口背景那张图是直接用的高斯模糊效果,并不是亚克力 Acrylic 效果。鉴于那张被模糊得看不清的图是我自己画的,所以我一定要单独放出来给大家看��!

我自己画的图,不忍直视,只好模糊掉作为背景了
▲ 我自己画的图,不忍直视,只好模糊掉作为背景了

话不多说看源码

UWP 里的 CompositionBrush 是用一个 ShaderEffect 做出所有控件的所有效果的。正如 叛逆者 在 如何评价微软在 Build 2017 上提出的 Fluent Design System? - 知乎 一文中说的,只需要极少的计算量就能完成。

不过 Win32 窗口并没有得到眷恋,所以我只好自己实现。但限于只能使用 WPF 内建机制,故性能上当然不能比了。但在小型项目的局部用用还是非常不错的——尤其是个人项目!不过话说现在个人项目谁还用 WPF 呢 (逃

思路是画一个径向渐变,即 RadialGradientBrush,然后当鼠标在窗口内移动时,改变径向渐变的渐变中心为鼠标所在点。

以下是全部源码。不要在意基类啦!WPF 不让我们实现自己的 Brush,所以只好用 MarkupExtension 绕道实现了。

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;

namespace Walterlv.Demo
{
    /// <summary>
    /// Paints a control border with a reveal effect using composition brush and light effects.
    /// </summary>
    public class RevealBorderBrushExtension : MarkupExtension
    {
        /// <summary>
        /// The color to use for rendering in case the <see cref="MarkupExtension"/> can't work correctly.
        /// </summary>
        public Color FallbackColor { get; set; } = Colors.White;

        /// <summary>
        /// Gets or sets a value that specifies the base background color for the brush.
        /// </summary>
        public Color Color { get; set; } = Colors.White;

        public Transform Transform { get; set; } = Transform.Identity;

        public Transform RelativeTransform { get; set; } = Transform.Identity;

        public double Opacity { get; set; } = 1.0;

        public double Radius { get; set; } = 100.0;

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            // 如果没有服务,则直接返回。
            if (!(serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget service)) return null;
            // MarkupExtension 在样式模板中,返回 this 以延迟提供值。
            if (service.TargetObject.ToString().EndsWith("SharedDp")) return this;
            if (!(service.TargetObject is FrameworkElement element)) return this;
            if (DesignerProperties.GetIsInDesignMode(element)) return new SolidColorBrush(FallbackColor);

            var window = Window.GetWindow(element);
            if (window == null) return this;
            var brush = CreateBrush(window, element);
            return brush;
        }

        private Brush CreateBrush(Window window, FrameworkElement element)
        {
            var brush = new RadialGradientBrush(Colors.White, Colors.Transparent)
            {
                MappingMode = BrushMappingMode.Absolute,
                RadiusX = Radius,
                RadiusY = Radius,
                Opacity = Opacity,
                Transform = Transform,
                RelativeTransform = RelativeTransform,
            };
            window.MouseMove += OnMouseMove;
            window.Closed += OnClosed;
            return brush;

            void OnMouseMove(object sender, MouseEventArgs e)
            {
                var position = e.GetPosition(element);
                brush.GradientOrigin = position;
                brush.Center = position;
            }

            void OnClosed(object o, EventArgs eventArgs)
            {
                window.MouseMove -= OnMouseMove;
                window.Closed -= OnClosed;
            }
        }
    }
}

参考资料

  • 突出显示 - UWP app developer - Microsoft Docs

相关文章:

  • 语义版本号(Semantic Versioning)
  • 使用 GitVersion 在编译或持续构建时自动使用语义版本号(Semantic Versioning)
  • UWP 流畅设计中的光照效果(容易的 RevealBorderBrush 和不那么容易的 RevealBackgroundBrush)
  • 使用 Emit 生成 IL 代码
  • 如何快速编写和调试 Emit 生成 IL 的代码
  • 自动将 NuGet 包的引用方式从 packages.config 升级为 PackageReference
  • 冷算法:自动生成代码标识符(类名、方法名、变量名)
  • WPF/UWP 的 Grid 布局竟然有 Bug,还不止一个!了解 Grid 中那些未定义的布局规则
  • Git 更安全的强制推送,--force-with-lease
  • 项目文件中的已知 NuGet 属性(使用这些属性,创建 NuGet 包就可以不需要 nuspec 文件啦)
  • 理解 C# 项目 csproj 文件格式的本质和编译流程
  • 如何创建一个基于命令行工具的跨平台的 NuGet 工具包
  • 如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包
  • 如何最快速地将旧的 NuGet 包 (2.x, packages.config) 升级成新的 NuGet 包 (4.x, PackageReference)
  • 每次都要重新编译?太慢!让跨平台的 MSBuild/dotnet build 的 Target 支持差量编译
  • “寒冬”下的金三银四跳槽季来了,帮你客观分析一下局面
  • Angular js 常用指令ng-if、ng-class、ng-option、ng-value、ng-click是如何使用的?
  • avalon2.2的VM生成过程
  • C++11: atomic 头文件
  • conda常用的命令
  • Java 多线程编程之:notify 和 wait 用法
  • JS创建对象模式及其对象原型链探究(一):Object模式
  • nginx 负载服务器优化
  • V4L2视频输入框架概述
  • yii2中session跨域名的问题
  • 翻译--Thinking in React
  • 构造函数(constructor)与原型链(prototype)关系
  • 一个SAP顾问在美国的这些年
  • 用简单代码看卷积组块发展
  • postgresql行列转换函数
  • 第二十章:异步和文件I/O.(二十三)
  • # 20155222 2016-2017-2 《Java程序设计》第5周学习总结
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • (2020)Java后端开发----(面试题和笔试题)
  • (html转换)StringEscapeUtils类的转义与反转义方法
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (六)c52学习之旅-独立按键
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (牛客腾讯思维编程题)编码编码分组打印下标(java 版本+ C版本)
  • (一)kafka实战——kafka源码编译启动
  • ../depcomp: line 571: exec: g++: not found
  • .Net - 类的介绍
  • .NET Core跨平台微服务学习资源
  • .netcore 如何获取系统中所有session_ASP.NET Core如何解决分布式Session一致性问题
  • .NET中 MVC 工厂模式浅析
  • .net最好用的JSON类Newtonsoft.Json获取多级数据SelectToken
  • .stream().map与.stream().flatMap的使用
  • //解决validator验证插件多个name相同只验证第一的问题
  • [20190416]完善shared latch测试脚本2.txt
  • [Angular 基础] - 指令(directives)
  • [CCIE历程]CCIE # 20604
  • [ChromeApp]指南!让你的谷歌浏览器好用十倍!
  • [dfs搜索寻找矩阵中最长递减序列]魔法森林的秘密路径
  • [emuch.net]MatrixComputations(7-12)