我用 WPF 做了一个 “苍蝇飞舞” 的屏保

目录

实现原理

核心流程

核心技术点

技术栈与依赖

下载


我本来想做漂亮的蝴蝶飞舞 ,奈何用WPF的矢量图形不好表现蝴蝶,画出来之后,反而像苍蝇在飞舞。


实现原理

基于 WPF 透明窗口 + 原生 GPU 渲染,实现全屏置顶、鼠标穿透的飞虫屏保效果。

效果:

窗口铺满屏幕,背景完全透明,可看到桌面、始终在最前层、点击、移动等操作直接作用于下层窗口、多只飞虫缓慢、柔和、随机飞行,带翅膀扇动效果

关闭方式:Ctrl+Shift+Q


核心流程

  1. **窗口**:WPF 透明窗口 + Win32 扩展样式实现全屏、置顶、鼠标穿透

  2. **动画**:`CompositionTarget.Rendering` 与显示刷新同步,每帧更新位置

  3. **绘制**:`OnRender` + `DrawingContext`,由 WPF 的 GPU 合成管线渲染

  4. **飞虫**:矢量翅膀(PathGeometry)或位图,带位置、角度、翅膀扇动


核心技术点

窗口控制:

cs 复制代码
<Window AllowsTransparency="True"
        WindowStyle="None"
        Background="Transparent"
        WindowState="Maximized"
        Topmost="True" />

鼠标穿透(WS_EX_TRANSPARENT)

通过 Win32 API 为窗口添加 `WS_EX_TRANSPARENT`,使鼠标事件穿透到下层:

cs 复制代码
private const int GWL_EXSTYLE = -20;
private const int WS_EX_TRANSPARENT = 0x00000020;

[DllImport("user32.dll")]
private static extern int GetWindowLong(IntPtr hwnd, int index);


[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);


var hwnd = new WindowInteropHelper(this).Handle;
var exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TRANSPARENT);

全局热键(因鼠标穿透无法接收键盘)

cs 复制代码
private const int WM_HOTKEY = 0x0312;

[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hwnd, int id, uint fsModifiers, uint vk);

// 注册 Ctrl+Shift+Q
RegisterHotKey(hwnd, HOTKEY_ID, MOD_CTRL | MOD_SHIFT, VK_Q);

// 在 WndProc 中处理
if (msg == WM_HOTKEY && wParam.ToInt32() == HOTKEY_ID)
    Close();

帧同步动画(CompositionTarget.Rendering)

cs 复制代码
System.Windows.Media.CompositionTarget.Rendering += OnRendering;
private void OnRendering(object? sender, EventArgs e)
{
    var args = (RenderingEventArgs)e;
    var now = args.RenderingTime.TotalSeconds;
    var dt = now - _lastUpdate;
    _lastUpdate = now;

    // 更新每只飞虫的位置、角度、翅膀相位
    foreach (var b in _butterflies)
    {
        b.X += b.Vx * dt;
        b.Y += b.Vy * dt;
        b.FlapPhase += dt * b.FlapSpeed;
    }
    InvalidateVisual();  // 触发重绘
}

自定义绘制(OnRender + DrawingContext)

cs 复制代码
protected override void OnRender(DrawingContext dc)
{
    foreach (var b in _butterflies)
    {
        var wingFlap = Math.Sin(b.FlapPhase) * 0.35;
        dc.PushTransform(new TranslateTransform(b.X, b.Y));
        dc.PushTransform(new RotateTransform(b.Angle * 180 / Math.PI, 0, 0));
        dc.PushTransform(new ScaleTransform(1, 1 - wingFlap * 0.15, centerX, centerY));

        // 绘制身体、翅膀(PathGeometry / Ellipse / Image)
        dc.DrawGeometry(brush, pen, wingGeometry);
        dc.Pop(); dc.Pop(); dc.Pop();
    }
}

翅膀矢量绘制(PathGeometry + BezierSegment)

cs 复制代码
private static Geometry CreateMonarchForewing(int side, double w, double h)
{
    var fig = new PathFigure { StartPoint = new Point(0, 0), IsClosed = true };
    fig.Segments.Add(new BezierSegment(
        new Point(side * w * 0.25, -h * 0.15),
        new Point(side * w * 0.85, -h * 0.55),
        new Point(side * w, -h * 0.25), true));
    fig.Segments.Add(new BezierSegment(
        new Point(side * w * 0.9, h * 0.05),
        new Point(side * w * 0.35, h * 0.55),
        new Point(0, h * 0.25), true));
    var geo = new PathGeometry();
    geo.Figures.Add(fig);
    geo.Freeze();
    return geo;
}

技术栈与依赖

框架 : .NET 8 + WPF

渲染 :WPF 原生 DrawingContext(GPU 加速)

透明 :AllowsTransparency + Background="Transparent"

|穿透 : Win32 WS_EX_TRANSPARENT

热键 :Win32 RegisterHotKey

依赖 :无第三方图形库(纯 WPF)


下载

附件下载: https://pan.baidu.com/s/1hLm8aRX0tQLUXAfl4uba9w 提取码: late

程序目录如下:

相关推荐
wuty00715 小时前
完善基于WPF开发的标尺控件(含实例代码)
wpf·wpf标尺·支持横向竖向标尺·ruler
mocoding20 小时前
使用Flutter设置UI三方库card_settings_ui重构鸿蒙版天气预报我的页面
flutter·ui·harmonyos
雨季66620 小时前
Flutter 三端应用实战:OpenHarmony 简易点击计数器与循环颜色反馈器开发指南
开发语言·flutter·ui·ecmascript·dart
雨季6661 天前
Flutter 三端应用实战:OpenHarmony 简易“动态主题切换卡片”交互模式
flutter·ui·交互·dart
浩浩测试一下1 天前
洪水猛兽攻击 Ddos Dos cc Drdos floods区别
安全·web安全·网络安全·系统安全·wpf·可信计算技术·安全架构
雨季6661 天前
Flutter 三端应用实战:OpenHarmony 简易“动态色盘生成器”交互模式深度解析
开发语言·前端·flutter·ui·交互
雨季6661 天前
Flutter 三端应用实战:OpenHarmony 简易“可展开任务详情卡片”交互模式深度解析
开发语言·前端·javascript·flutter·ui·交互
GIS小小研究僧1 天前
如何使用Photoshop扣透明底电子签名
ui·photoshop
无心水1 天前
分布式环境下定时任务与SELECT FOR UPDATE的陷阱与解决方案
分布式·后端·wpf·xxl-job·quartz·定时任务·selectforupdate