实现提供了完整的弹幕功能,包括多行显示、不同颜色、头像支持、防重叠、椭圆形边框、透明度渐变、点击事件、字体样式设置、暂停/继续功能、过滤功能等。还包含了批处理队列处理逻辑、速率限制、错误处理和性能监控等功能。
This commit is contained in:
215
YwxAppWpfDanMu/Controls/DanMuControl.xaml.cs
Normal file
215
YwxAppWpfDanMu/Controls/DanMuControl.xaml.cs
Normal file
@@ -0,0 +1,215 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using YwxAppWpfDanMu.Models;
|
||||
using YwxAppWpfDanMu.Services;
|
||||
using YwxAppWpfDanMu.Utils;
|
||||
|
||||
namespace YwxAppWpfDanMu.Controls
|
||||
{
|
||||
public partial class DanMuControl : UserControl
|
||||
{
|
||||
private readonly DanMuPool _danMuPool;
|
||||
private readonly DanMuDispatcher _dispatcher;
|
||||
private readonly DanMuQueueProcessor _queueProcessor;
|
||||
private readonly RateLimiter _rateLimiter;
|
||||
private readonly DanMuPerformanceMonitor _performanceMonitor;
|
||||
|
||||
internal List<DanMuTrack> _tracks = new List<DanMuTrack>();
|
||||
private readonly Dictionary<DanMuItem, DanMuTrack> _activeItems = new Dictionary<DanMuItem, DanMuTrack>();
|
||||
|
||||
public DanMuControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_danMuPool = new DanMuPool(this);
|
||||
_dispatcher = new DanMuDispatcher(Dispatcher);
|
||||
_queueProcessor = new DanMuQueueProcessor(_dispatcher);
|
||||
_rateLimiter = new RateLimiter(50); // 限制每秒50条
|
||||
_performanceMonitor = new DanMuPerformanceMonitor();
|
||||
|
||||
Loaded += OnLoaded;
|
||||
Unloaded += OnUnloaded;
|
||||
SizeChanged += OnSizeChanged;
|
||||
}
|
||||
|
||||
#region Dependency Properties
|
||||
|
||||
public static readonly DependencyProperty LineCountProperty = DependencyProperty.Register(
|
||||
"LineCount", typeof(int), typeof(DanMuControl), new PropertyMetadata(5, OnLineCountChanged));
|
||||
|
||||
public int LineCount
|
||||
{
|
||||
get => (int)GetValue(LineCountProperty);
|
||||
set => SetValue(LineCountProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsPausedProperty = DependencyProperty.Register(
|
||||
"IsPaused", typeof(bool), typeof(DanMuControl), new PropertyMetadata(false));
|
||||
|
||||
public bool IsPaused
|
||||
{
|
||||
get => (bool)GetValue(IsPausedProperty);
|
||||
set => SetValue(IsPausedProperty, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler<DanMuEventArgs> DanMuClick;
|
||||
public event EventHandler<DanMuEventArgs> DanMuAdded;
|
||||
public event EventHandler<DanMuEventArgs> DanMuRemoved;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void AddDanMu(DanMuMessage message)
|
||||
{
|
||||
if (!_rateLimiter.TryAcquire())
|
||||
{
|
||||
// 超出速率限制,可以记录日志或采取其他措施
|
||||
return;
|
||||
}
|
||||
|
||||
_queueProcessor.Enqueue(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (DanMuFilter.ShouldFilter(message))
|
||||
return;
|
||||
|
||||
var args = new DanMuEventArgs(message);
|
||||
DanMuAdded?.Invoke(this, args);
|
||||
|
||||
if (!args.Handled)
|
||||
{
|
||||
_danMuPool.AddDanMu(message);
|
||||
}
|
||||
|
||||
_performanceMonitor.RecordAddedDanMu();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 记录错误日志
|
||||
_performanceMonitor.RecordError(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void AddDanMuBatch(IEnumerable<DanMuMessage> messages)
|
||||
{
|
||||
_queueProcessor.EnqueueBatch(() =>
|
||||
{
|
||||
foreach (var message in messages)
|
||||
{
|
||||
if (!_rateLimiter.TryAcquire())
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
if (DanMuFilter.ShouldFilter(message))
|
||||
continue;
|
||||
|
||||
var args = new DanMuEventArgs(message);
|
||||
DanMuAdded?.Invoke(this, args);
|
||||
|
||||
if (!args.Handled)
|
||||
{
|
||||
_danMuPool.AddDanMu(message);
|
||||
}
|
||||
|
||||
_performanceMonitor.RecordAddedDanMu();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_performanceMonitor.RecordError(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void ClearAll()
|
||||
{
|
||||
_queueProcessor.Enqueue(() =>
|
||||
{
|
||||
_danMuPool.ClearAll();
|
||||
_performanceMonitor.RecordClear();
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
InitializeTracks();
|
||||
_danMuPool.Start();
|
||||
_performanceMonitor.Start();
|
||||
}
|
||||
|
||||
private void OnUnloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_danMuPool.Stop();
|
||||
_performanceMonitor.Stop();
|
||||
}
|
||||
|
||||
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
InitializeTracks();
|
||||
}
|
||||
|
||||
private void InitializeTracks()
|
||||
{
|
||||
_tracks.Clear();
|
||||
|
||||
double trackHeight = ActualHeight / LineCount;
|
||||
for (int i = 0; i < LineCount; i++)
|
||||
{
|
||||
_tracks.Add(new DanMuTrack
|
||||
{
|
||||
Top = i * trackHeight,
|
||||
Height = trackHeight,
|
||||
Width = ActualWidth
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnLineCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is DanMuControl control)
|
||||
{
|
||||
control.InitializeTracks();
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnDanMuItemClick(DanMuItem item)
|
||||
{
|
||||
var args = new DanMuEventArgs(item.Message);
|
||||
DanMuClick?.Invoke(this, args);
|
||||
}
|
||||
|
||||
internal void OnDanMuItemRemoved(DanMuItem item)
|
||||
{
|
||||
var args = new DanMuEventArgs(item.Message);
|
||||
DanMuRemoved?.Invoke(this, args);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal class DanMuTrack
|
||||
{
|
||||
public double Top { get; set; }
|
||||
public double Height { get; set; }
|
||||
public double Width { get; set; }
|
||||
public double AvailablePosition { get; set; }
|
||||
public List<DanMuItem> ActiveItems { get; } = new List<DanMuItem>();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user