我用 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

程序目录如下:

相关推荐
传奇开心果编程15 小时前
【SwiftUI提高练中学】第13课 实时活动与灵动岛:ActivityKit 实战
学习·ui·ios·swiftui·swift
samble17 小时前
从零搭建灌装监控系统(十):生产追溯,条码触发自动记录
c#·wpf·mvvm·modbus·工业控制
phltxy20 小时前
C 语言动态内存管理:从申请空间到安全释放
c语言·开发语言·ui
李少兄1 天前
文档排版与数据处理:阿拉伯数字与罗马数字的转换
ui·xhtml·wps
狗凯之家源码网1 天前
网址导航系统源码评测:个性 UI 与轻量化架构实战
ui·架构·网址导航系统
传奇开心果编程1 天前
【SwiftUI入门练中学】第17课 通知与后台任务
学习·ui·ios·swiftui·swift
wshzd2 天前
LLM之Agent(107)|DeepSeek-Harness(十五)流式输出管道:从 StreamChunk 到 UI
人工智能·ui
我命由我123452 天前
Photoshop - Photoshop 快速共享作品
学习·ui·职场和发展·求职招聘·职场发展·学习方法·photoshop
我命由我123452 天前
Photoshop - 将Photoshop图像置入其他应用程序
学习·ui·职场和发展·求职招聘·职场发展·学习方法·photoshop
bloglin999993 天前
豆包接口返回UI元素原因
ui