improve:墨迹渐隐

This commit is contained in:
2025-09-06 13:16:10 +08:00
parent 41bcae9d05
commit 1a9ddf1969
6 changed files with 757 additions and 65 deletions
+54 -6
View File
@@ -833,12 +833,8 @@ namespace Ink_Canvas.Helpers
// 归一化方向向量
direction.Normalize();
// 使用OpacityMask创建更自然的擦除效果
var maskBrush = CreateEraseMaskBrush(stroke, strokeStart, strokeEnd, 1.0);
visual.OpacityMask = maskBrush;
// 创建擦除动画 - 使用定时器来更新遮罩
StartEraseMaskAnimation(visual, stroke, strokeStart, strokeEnd, duration);
// 创建精确的擦除动画 - 使用PathGeometry来避免变形
CreatePreciseEraseAnimation(visual, stroke, bounds, strokeStart, strokeEnd, direction, length, duration);
}
catch (Exception ex)
{
@@ -848,6 +844,58 @@ namespace Ink_Canvas.Helpers
}
}
/// <summary>
/// 创建精确的擦除动画 - 使用动态裁剪区域避免墨迹移动
/// </summary>
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);
}
}
/// <summary>
/// 动画完成后的统一处理
/// </summary>
+43 -56
View File
@@ -155,6 +155,22 @@ namespace Ink_Canvas.Helpers
{
// 更新创建时间,使其成为最新的窗口
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();
}
}
@@ -165,64 +181,12 @@ namespace Ink_Canvas.Helpers
/// </summary>
private static void ApplyZOrder()
{
// 按创建时间排序,最新的窗口在最后
var sortedWindows = _windowStack
.Where(w => IsWindow(w.Handle) && IsWindowVisible(w.Handle) && !IsIconic(w.Handle))
.OrderBy(w => w.CreatedTime)
.ToList();
if (sortedWindows.Count == 0) return;
// 获取主窗口(第一个注册的窗口)
var mainWindow = sortedWindows.FirstOrDefault();
if (mainWindow == null) return;
// 如果主窗口需要置顶且启用了无焦点模式
if (mainWindow.IsTopmost && mainWindow.IsNoFocusMode)
// 简化逻辑:直接设置所有窗口为置顶,让Windows系统自然处理层级
foreach (var windowInfo in _windowStack.ToList())
{
// 检查是否有子窗口在前景
var foregroundWindow = GetForegroundWindow();
var hasChildWindowInForeground = false;
if (foregroundWindow != mainWindow.Handle)
{
var foregroundWindowProcessId = GetWindowThreadProcessId(foregroundWindow, out uint processId);
var currentProcessId = GetCurrentProcessId();
if (processId == currentProcessId)
{
// 检查前景窗口是否在我们的窗口列表中
var foregroundWindowInfo = sortedWindows.FirstOrDefault(w => w.Handle == foregroundWindow);
if (foregroundWindowInfo != null)
{
hasChildWindowInForeground = true;
}
}
}
if (!hasChildWindowInForeground)
{
// 没有子窗口在前景,主窗口置顶
SetWindowPos(mainWindow.Handle, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
// 确保主窗口样式正确
int exStyle = GetWindowLong(mainWindow.Handle, GWL_EXSTYLE);
if ((exStyle & WS_EX_TOPMOST) == 0)
{
SetWindowLong(mainWindow.Handle, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
}
}
}
// 处理其他窗口的层级
for (int i = 1; i < sortedWindows.Count; i++)
{
var windowInfo = sortedWindows[i];
// 子窗口应该置顶于主窗口
if (windowInfo.IsTopmost)
if (windowInfo.IsTopmost && IsWindow(windowInfo.Handle) && IsWindowVisible(windowInfo.Handle) && !IsIconic(windowInfo.Handle))
{
// 设置窗口为置顶
SetWindowPos(windowInfo.Handle, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
@@ -273,5 +237,28 @@ namespace Ink_Canvas.Helpers
return _windowStack.Count;
}
}
/// <summary>
/// 强制刷新所有窗口的置顶状态
/// </summary>
public static void ForceRefreshAllWindows()
{
lock (_lockObject)
{
foreach (var windowInfo in _windowStack.ToList())
{
if (windowInfo.IsTopmost && IsWindow(windowInfo.Handle))
{
// 强制设置窗口为置顶
SetWindowPos(windowInfo.Handle, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
// 确保窗口样式正确
int exStyle = GetWindowLong(windowInfo.Handle, GWL_EXSTYLE);
SetWindowLong(windowInfo.Handle, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
}
}
}
}
}
}