实现提供了完整的弹幕功能,包括多行显示、不同颜色、头像支持、防重叠、椭圆形边框、透明度渐变、点击事件、字体样式设置、暂停/继续功能、过滤功能等。还包含了批处理队列处理逻辑、速率限制、错误处理和性能监控等功能。
This commit is contained in:
152
YwxAppWpfDanMu/Controls/DanMuPool.cs
Normal file
152
YwxAppWpfDanMu/Controls/DanMuPool.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Threading;
|
||||
using YwxAppWpfDanMu.Models;
|
||||
|
||||
namespace YwxAppWpfDanMu.Controls
|
||||
{
|
||||
internal class DanMuPool
|
||||
{
|
||||
private readonly DanMuControl _parent;
|
||||
private readonly Queue<DanMuMessage> _messageQueue = new Queue<DanMuMessage>();
|
||||
private readonly DispatcherTimer _timer;
|
||||
private readonly Random _random = new Random();
|
||||
|
||||
public DanMuPool(DanMuControl parent)
|
||||
{
|
||||
_parent = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||
|
||||
_timer = new DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromMilliseconds(50)
|
||||
};
|
||||
_timer.Tick += OnTimerTick;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_timer.Stop();
|
||||
}
|
||||
|
||||
public void AddDanMu(DanMuMessage message)
|
||||
{
|
||||
lock (_messageQueue)
|
||||
{
|
||||
_messageQueue.Enqueue(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearAll()
|
||||
{
|
||||
lock (_messageQueue)
|
||||
{
|
||||
_messageQueue.Clear();
|
||||
}
|
||||
|
||||
_parent.DanMuCanvas.Children.Clear();
|
||||
foreach (var track in _parent._tracks)
|
||||
{
|
||||
track.ActiveItems.Clear();
|
||||
track.AvailablePosition = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTimerTick(object sender, EventArgs e)
|
||||
{
|
||||
if (_parent.IsPaused)
|
||||
return;
|
||||
|
||||
lock (_messageQueue)
|
||||
{
|
||||
while (_messageQueue.Count > 0)
|
||||
{
|
||||
var message = _messageQueue.Dequeue();
|
||||
DispatchDanMu(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DispatchDanMu(DanMuMessage message)
|
||||
{
|
||||
// 找到最适合的轨道
|
||||
var track = FindBestTrack(message);
|
||||
if (track == null)
|
||||
return;
|
||||
|
||||
var danMuItem = new DanMuItem(message);
|
||||
danMuItem.Click += (s, args) => _parent.OnDanMuItemClick(danMuItem);
|
||||
|
||||
// 测量弹幕宽度
|
||||
danMuItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||||
var itemWidth = danMuItem.DesiredSize.Width;
|
||||
|
||||
// 设置初始位置
|
||||
Canvas.SetLeft(danMuItem, _parent.ActualWidth);
|
||||
Canvas.SetTop(danMuItem, track.Top + (track.Height - danMuItem.DesiredSize.Height) / 2);
|
||||
|
||||
_parent.DanMuCanvas.Children.Add(danMuItem);
|
||||
track.ActiveItems.Add(danMuItem);
|
||||
|
||||
// 创建动画
|
||||
var animation = new DoubleAnimation
|
||||
{
|
||||
From = _parent.ActualWidth,
|
||||
To = -itemWidth,
|
||||
Duration = TimeSpan.FromSeconds(5 + _random.NextDouble() * 3),
|
||||
FillBehavior = FillBehavior.Stop
|
||||
};
|
||||
|
||||
animation.Completed += (s, args) =>
|
||||
{
|
||||
_parent.DanMuCanvas.Children.Remove(danMuItem);
|
||||
track.ActiveItems.Remove(danMuItem);
|
||||
_parent.OnDanMuItemRemoved(danMuItem);
|
||||
};
|
||||
|
||||
// 添加透明度渐变效果
|
||||
var opacityAnimation = new DoubleAnimation
|
||||
{
|
||||
From = 1.0,
|
||||
To = 0.2,
|
||||
Duration = animation.Duration,
|
||||
BeginTime = TimeSpan.FromSeconds(animation.Duration.TimeSpan.TotalSeconds * 0.7)
|
||||
};
|
||||
|
||||
danMuItem.BeginAnimation(Canvas.LeftProperty, animation);
|
||||
danMuItem.BeginAnimation(UIElement.OpacityProperty, opacityAnimation);
|
||||
|
||||
// 更新轨道可用位置
|
||||
track.AvailablePosition = _parent.ActualWidth + itemWidth + 10; // 10为间距
|
||||
}
|
||||
|
||||
private DanMuControl.DanMuTrack FindBestTrack(DanMuMessage message)
|
||||
{
|
||||
if (_parent._tracks.Count == 0)
|
||||
return null;
|
||||
|
||||
// 随机选择一个轨道,检查是否有足够空间
|
||||
int startIndex = _random.Next(_parent._tracks.Count);
|
||||
for (int i = 0; i < _parent._tracks.Count; i++)
|
||||
{
|
||||
int index = (startIndex + i) % _parent._tracks.Count;
|
||||
var track = _parent._tracks[index];
|
||||
|
||||
// 如果轨道上没有弹幕,或者最后一个弹幕已经移动足够远
|
||||
if (track.ActiveItems.Count == 0 ||
|
||||
track.AvailablePosition < _parent.ActualWidth * 0.7)
|
||||
{
|
||||
return track;
|
||||
}
|
||||
}
|
||||
|
||||
// 所有轨道都满了,选择最不拥挤的轨道
|
||||
return _parent._tracks.OrderBy(t => t.ActiveItems.Count).First();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user