目录

我本来想做漂亮的蝴蝶飞舞
,奈何用WPF的矢量图形不好表现蝴蝶,画出来之后,反而像苍蝇在飞舞。
实现原理
基于 WPF 透明窗口 + 原生 GPU 渲染,实现全屏置顶、鼠标穿透的飞虫屏保效果。

效果:
窗口铺满屏幕,背景完全透明,可看到桌面、始终在最前层、点击、移动等操作直接作用于下层窗口、多只飞虫缓慢、柔和、随机飞行,带翅膀扇动效果
关闭方式:Ctrl+Shift+Q
核心流程
-
**窗口**:WPF 透明窗口 + Win32 扩展样式实现全屏、置顶、鼠标穿透
-
**动画**:`CompositionTarget.Rendering` 与显示刷新同步,每帧更新位置
-
**绘制**:`OnRender` + `DrawingContext`,由 WPF 的 GPU 合成管线渲染
-
**飞虫**:矢量翅膀(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
程序目录如下:

