From 9081aea9261d03dd6cd0c9711707a1f59e4f7b1a Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 6 Sep 2025 13:48:44 +0800
Subject: [PATCH] =?UTF-8?q?improve:=E5=A2=A8=E8=BF=B9=E6=B8=90=E9=9A=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Helpers/InkFadeManager.cs | 158 +++++++-----------
Ink Canvas/Helpers/WindowZOrderManager.cs | 47 +++---
Ink Canvas/MainWindow.xaml.cs | 56 +++++--
.../MainWindow_cs/MW_FloatingBarIcons.cs | 3 +-
Ink Canvas/MainWindow_cs/MW_TrayIcon.cs | 7 +-
Ink Canvas/Windows/RandWindow.xaml.cs | 27 +--
6 files changed, 137 insertions(+), 161 deletions(-)
diff --git a/Ink Canvas/Helpers/InkFadeManager.cs b/Ink Canvas/Helpers/InkFadeManager.cs
index eec846ac..a13d1ea6 100644
--- a/Ink Canvas/Helpers/InkFadeManager.cs
+++ b/Ink Canvas/Helpers/InkFadeManager.cs
@@ -6,6 +6,7 @@ using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
+using System.Windows.Media.Effects;
using System.Windows.Shapes;
using System.Windows.Threading;
@@ -324,6 +325,13 @@ namespace Ink_Canvas.Helpers
{
path.StrokeThickness = Math.Max(drawingAttribs.Width * 1.5, 20);
}
+
+ // 为高亮笔添加轻微的模糊效果,使渐隐更加自然
+ path.Effect = new BlurEffect
+ {
+ Radius = 0.5, // 轻微的模糊效果
+ KernelType = KernelType.Gaussian
+ };
}
// 不设置任何变换,保持墨迹原有粗细
@@ -381,8 +389,7 @@ namespace Ink_Canvas.Helpers
{
try
{
- // 对于普通墨迹也使用连续渐隐动画
- StartContinuousFadeAnimation(visual, stroke, currentOpacity, AnimationDuration);
+ StartProgressiveFadeAnimation(visual, stroke, currentOpacity, AnimationDuration);
}
catch (Exception ex)
{
@@ -390,6 +397,57 @@ namespace Ink_Canvas.Helpers
}
}
+ ///
+ /// 统一渐隐动画 - 整个墨迹作为一个整体进行渐隐,与擦除效果一致
+ ///
+ private void StartUnifiedFadeAnimation(UIElement visual, Stroke stroke, double currentOpacity, int duration)
+ {
+ try
+ {
+ // 创建透明度动画,模拟擦除时的效果
+ var fadeAnimation = new DoubleAnimation
+ {
+ From = currentOpacity,
+ To = 0.0,
+ Duration = TimeSpan.FromMilliseconds(duration),
+ EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut }
+ };
+
+ // 如果是高亮笔,添加轻微的缩放效果,使渐隐更加自然
+ if (stroke.DrawingAttributes.IsHighlighter)
+ {
+ // 创建轻微的缩放动画,模拟墨迹"蒸发"的效果
+ var scaleAnimation = new DoubleAnimation
+ {
+ From = 1.0,
+ To = 0.95, // 轻微缩小,增加自然感
+ Duration = TimeSpan.FromMilliseconds(duration),
+ EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseIn }
+ };
+
+ // 创建缩放变换
+ var scaleTransform = new ScaleTransform();
+ visual.RenderTransform = scaleTransform;
+ visual.RenderTransformOrigin = new Point(0.5, 0.5);
+
+ // 应用缩放动画
+ scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnimation);
+ scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnimation);
+ }
+
+ // 添加动画完成事件
+ fadeAnimation.Completed += (sender, e) => OnAnimationCompleted(visual, stroke);
+
+ // 应用透明度动画
+ visual.BeginAnimation(UIElement.OpacityProperty, fadeAnimation);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"统一渐隐动画失败: {ex}", LogHelper.LogType.Error);
+ OnAnimationCompleted(visual, stroke);
+ }
+ }
+
///
/// 开始高亮笔的渐隐动画
///
@@ -397,8 +455,8 @@ namespace Ink_Canvas.Helpers
{
try
{
- // 使用连续渐隐动画而不是分段动画
- StartContinuousFadeAnimation(visual, stroke, currentOpacity, (int)(AnimationDuration * 1.5));
+ // 高亮笔使用统一的渐隐动画,与擦除效果一致
+ StartUnifiedFadeAnimation(visual, stroke, currentOpacity, (int)(AnimationDuration * 1.2));
}
catch (Exception ex)
{
@@ -804,98 +862,6 @@ namespace Ink_Canvas.Helpers
return curve;
}
- ///
- /// 连续渐隐动画 - 模拟橡皮擦擦除效果
- ///
- private void StartContinuousFadeAnimation(UIElement visual, Stroke stroke, double currentOpacity, int duration)
- {
- try
- {
- // 获取墨迹的边界
- var geometry = stroke.GetGeometry();
- if (geometry == null) return;
-
- var bounds = geometry.Bounds;
- var strokeStart = stroke.StylusPoints[0].ToPoint();
- var strokeEnd = stroke.StylusPoints[stroke.StylusPoints.Count - 1].ToPoint();
-
- // 计算墨迹的方向向量
- var direction = new Vector(strokeEnd.X - strokeStart.X, strokeEnd.Y - strokeStart.Y);
- var length = direction.Length;
-
- if (length == 0)
- {
- // 如果墨迹没有方向(单点),使用简单渐隐
- StartSimpleFadeAnimation(visual, stroke, currentOpacity, duration);
- return;
- }
-
- // 归一化方向向量
- direction.Normalize();
-
- // 创建精确的擦除动画 - 使用PathGeometry来避免变形
- CreatePreciseEraseAnimation(visual, stroke, bounds, strokeStart, strokeEnd, direction, length, duration);
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"连续渐隐动画失败: {ex}", LogHelper.LogType.Error);
- // 失败时回退到简单动画
- StartSimpleFadeAnimation(visual, stroke, currentOpacity, duration);
- }
- }
-
- ///
- /// 创建精确的擦除动画 - 使用动态裁剪区域避免墨迹移动
- ///
- private void CreatePreciseEraseAnimation(UIElement visual, Stroke stroke, Rect bounds, Point strokeStart, Point strokeEnd, Vector direction, double length, int duration)
- {
- try
- {
- // 计算擦除方向上的移动距离
- var totalDistance = Math.Sqrt(Math.Pow(strokeEnd.X - strokeStart.X, 2) + Math.Pow(strokeEnd.Y - strokeStart.Y, 2));
-
- if (totalDistance == 0)
- {
- // 如果墨迹没有长度,使用简单渐隐
- StartSimpleFadeAnimation(visual, stroke, visual.Opacity, duration);
- return;
- }
-
- // 创建动态裁剪区域 - 从完整墨迹开始,逐渐缩小
- var clipGeometry = new RectangleGeometry
- {
- Rect = bounds
- };
-
- visual.Clip = clipGeometry;
-
- // 创建擦除动画 - 裁剪区域从起点向终点移动并缩小
- // 使用更自然的擦除效果:从起点开始,逐渐向终点移动
- var eraseAnimation = new RectAnimation
- {
- From = bounds, // 从完整边界开始
- To = new Rect(strokeEnd.X, strokeEnd.Y, 0, bounds.Height), // 到终点位置,宽度为0
- Duration = TimeSpan.FromMilliseconds(duration),
- EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut }
- };
-
- // 添加动画完成事件
- eraseAnimation.Completed += (sender, e) =>
- {
- OnAnimationCompleted(visual, stroke);
- };
-
- // 开始动画
- clipGeometry.BeginAnimation(RectangleGeometry.RectProperty, eraseAnimation);
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"精确擦除动画失败: {ex}", LogHelper.LogType.Error);
- // 失败时回退到简单动画
- StartSimpleFadeAnimation(visual, stroke, visual.Opacity, duration);
- }
- }
-
///
/// 动画完成后的统一处理
///
diff --git a/Ink Canvas/Helpers/WindowZOrderManager.cs b/Ink Canvas/Helpers/WindowZOrderManager.cs
index 898b0c06..2ce292c8 100644
--- a/Ink Canvas/Helpers/WindowZOrderManager.cs
+++ b/Ink Canvas/Helpers/WindowZOrderManager.cs
@@ -148,31 +148,30 @@ namespace Ink_Canvas.Helpers
{
if (window == null) return;
- lock (_lockObject)
+ try
{
- var windowInfo = _windowStack.FirstOrDefault(w => w.Window == window);
- if (windowInfo != null)
- {
- // 更新创建时间,使其成为最新的窗口
- windowInfo.CreatedTime = DateTime.Now;
-
- // 立即将窗口置顶
- var hwnd = new WindowInteropHelper(window).Handle;
- if (hwnd != IntPtr.Zero)
- {
- SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
- SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
-
- // 确保窗口样式正确
- int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
- if ((exStyle & WS_EX_TOPMOST) == 0)
- {
- SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
- }
- }
-
- ApplyZOrder();
- }
+ var hwnd = new WindowInteropHelper(window).Handle;
+ if (hwnd == IntPtr.Zero) return;
+
+ // 使用更直接的方法:先激活窗口,再置顶
+ window.Activate();
+ window.Focus();
+
+ // 设置窗口为置顶
+ SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
+
+ // 确保窗口样式正确
+ int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
+ SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
+
+ // 再次确保置顶
+ SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"BringToTop失败: {ex.Message}", LogHelper.LogType.Error);
}
}
diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs
index fc40ba2b..9cb41455 100644
--- a/Ink Canvas/MainWindow.xaml.cs
+++ b/Ink Canvas/MainWindow.xaml.cs
@@ -1751,18 +1751,22 @@ namespace Ink_Canvas
{
try
{
+ var hwnd = new WindowInteropHelper(this).Handle;
if (Settings.Advanced.IsAlwaysOnTop)
{
- // 注册到Z-Order管理器
- WindowZOrderManager.RegisterWindow(this, true, Settings.Advanced.IsNoFocusMode);
-
// 先设置WPF的Topmost属性
Topmost = true;
- // 立即应用置顶
- WindowZOrderManager.BringToTop(this);
+ // 使用更强的Win32 API调用来确保置顶
+ // 1. 设置窗口样式为置顶
+ int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
+ SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
- // 如果启用了无焦点模式,需要特殊处理
+ // 2. 使用SetWindowPos确保窗口在最顶层
+ SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
+
+ // 3. 如果启用了无焦点模式,需要特殊处理
if (Settings.Advanced.IsNoFocusMode)
{
// 启动置顶维护定时器
@@ -1777,9 +1781,15 @@ namespace Ink_Canvas
else
{
// 取消置顶时
- WindowZOrderManager.SetWindowTopmost(this, false);
-
- // 停止置顶维护定时器
+ // 1. 先使用Win32 API取消置顶
+ SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
+
+ // 2. 移除置顶窗口样式
+ int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
+ SetWindowLong(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_TOPMOST);
+
+ // 3. 停止置顶维护定时器
StopTopmostMaintenance();
// 注意:这里不直接设置Topmost,让其他代码根据模式决定
@@ -1848,14 +1858,30 @@ namespace Ink_Canvas
return;
}
- // 清理无效的窗口记录
- WindowZOrderManager.CleanupInvalidWindows();
-
// 检查是否有子窗口在前景
- if (!WindowZOrderManager.HasChildWindowInForeground())
+ var foregroundWindow = GetForegroundWindow();
+ if (foregroundWindow != hwnd)
{
- // 没有子窗口在前景,强制刷新所有窗口的置顶状态
- WindowZOrderManager.ForceRefreshAllWindows();
+ // 检查前景窗口是否是当前应用程序的子窗口
+ var foregroundWindowProcessId = GetWindowThreadProcessId(foregroundWindow, out uint processId);
+ var currentProcessId = GetCurrentProcessId();
+
+ if (processId == currentProcessId)
+ {
+ // 如果有子窗口在前景,暂停置顶维护
+ return;
+ }
+
+ // 如果窗口不在最顶层且没有子窗口,重新设置置顶
+ SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
+
+ // 确保窗口样式正确
+ int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
+ if ((exStyle & WS_EX_TOPMOST) == 0)
+ {
+ SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
+ }
}
}
catch (Exception ex)
diff --git a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
index a2dfdaa1..3b976f54 100644
--- a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
+++ b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
@@ -948,7 +948,8 @@ namespace Ink_Canvas
randWindow.Show();
// 确保窗口显示后立即置顶
randWindow.Activate();
- WindowZOrderManager.BringToTop(randWindow);
+ randWindow.Topmost = true;
+ randWindow.Focus();
}
public void CheckEraserTypeTab()
diff --git a/Ink Canvas/MainWindow_cs/MW_TrayIcon.cs b/Ink Canvas/MainWindow_cs/MW_TrayIcon.cs
index 0b78d802..21a9de2b 100644
--- a/Ink Canvas/MainWindow_cs/MW_TrayIcon.cs
+++ b/Ink Canvas/MainWindow_cs/MW_TrayIcon.cs
@@ -30,11 +30,10 @@ namespace Ink_Canvas
var mainWin = (MainWindow)Current.MainWindow;
if (mainWin.IsLoaded)
{
- // 通知Z-Order管理器有系统菜单打开
- // 这会导致主窗口暂时取消置顶,让系统菜单能够正常显示
+ // 在无焦点模式下,暂时取消主窗口置顶,让系统菜单能够正常显示
if (Ink_Canvas.MainWindow.Settings.Advanced.IsAlwaysOnTop && Ink_Canvas.MainWindow.Settings.Advanced.IsNoFocusMode)
{
- WindowZOrderManager.SetWindowTopmost(mainWin, false);
+ mainWin.Topmost = false;
}
// 判斷是否在收納模式中
@@ -72,7 +71,7 @@ namespace Ink_Canvas
// 菜单关闭后,恢复主窗口的置顶状态
if (Ink_Canvas.MainWindow.Settings.Advanced.IsAlwaysOnTop && Ink_Canvas.MainWindow.Settings.Advanced.IsNoFocusMode)
{
- WindowZOrderManager.SetWindowTopmost(mainWin, true);
+ mainWin.Topmost = true;
}
}
}
diff --git a/Ink Canvas/Windows/RandWindow.xaml.cs b/Ink Canvas/Windows/RandWindow.xaml.cs
index d62a36d8..66ecd8df 100644
--- a/Ink Canvas/Windows/RandWindow.xaml.cs
+++ b/Ink Canvas/Windows/RandWindow.xaml.cs
@@ -30,14 +30,11 @@ namespace Ink_Canvas
// 加载背景
LoadBackground(settings);
- // 注册到Z-Order管理器,确保窗口能够正确置顶
- WindowZOrderManager.RegisterWindow(this, true, false);
+ // 设置窗口为置顶
+ Topmost = true;
// 添加窗口关闭事件处理
Closed += RandWindow_Closed;
-
- // 添加窗口显示事件处理
- Loaded += RandWindow_Loaded;
}
private void LoadBackground(Settings settings)
@@ -84,14 +81,11 @@ namespace Ink_Canvas
// 加载背景
LoadBackground(settings);
- // 注册到Z-Order管理器,确保窗口能够正确置顶
- WindowZOrderManager.RegisterWindow(this, true, false);
+ // 设置窗口为置顶
+ Topmost = true;
// 添加窗口关闭事件处理
Closed += RandWindow_Closed;
-
- // 添加窗口显示事件处理
- Loaded += RandWindow_Loaded;
new Thread(() =>
{
@@ -355,22 +349,13 @@ namespace Ink_Canvas
}
}
- ///
- /// 窗口加载事件处理
- ///
- private void RandWindow_Loaded(object sender, RoutedEventArgs e)
- {
- // 窗口加载完成后,立即将其置顶
- WindowZOrderManager.BringToTop(this);
- }
-
///
/// 窗口关闭事件处理
///
private void RandWindow_Closed(object sender, EventArgs e)
{
- // 从Z-Order管理器中移除窗口
- WindowZOrderManager.UnregisterWindow(this);
+ // 窗口关闭时的清理工作
+ // 这里可以添加必要的清理代码
}
}
}