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 _messageQueue = new Queue(); 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(); } } }