?WPF 實現彈幕效果
控件名:BarrageExample
作者:WPFDevelopersOrg
原文鏈接:? ?https://github.com/WPFDevelopersOrg/WPFDevelopers
框架使用大于等于
.NET40
;Visual Studio 2022
;項目使用 MIT 開源許可協議;
此篇代碼目的只是為了分享思路
實現基礎彈幕一定是要使用
Canvas
比較簡單,只需實現Left
動畫從右到左。彈幕消息使用
Border
做彈幕背景。內容使用
TextBlock
做消息文本展示。當動畫執行完成默認移除
Canvas
中的彈幕控件。使用這種方式去加載彈幕
GPU
會占較高。
1) 準備BarrageExample.xaml如下:
<UserControl?x:Class="WPFDevelopers.Samples.ExampleViews.BarrageExample"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"?xmlns:d="http://schemas.microsoft.com/expression/blend/2008"?xmlns:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers"xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"mc:Ignorable="d"?d:DesignHeight="450"?d:DesignWidth="800"><Grid><Grid.RowDefinitions><RowDefinition/><RowDefinition?Height="Auto"/></Grid.RowDefinitions><Canvas?Name="MyCanvas"?Background="Transparent"></Canvas><Grid?Grid.Row="1"?Name="MyGrid"><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition?Width="Auto"/></Grid.ColumnDefinitions><TextBox?wpfdev:ElementHelper.IsWatermark="True"x:Name="tbBarrage"wpfdev:ElementHelper.Watermark="請彈幕內容"/><Button?Grid.Column="1"?Style="{StaticResource?PrimaryButton}"Content="發射彈幕"?Margin="4,0,0,0"?Click="ButtonBase_OnClick"/></Grid></Grid>
</UserControl>
2) 邏輯BarrageExample.xaml.cs如下:
using?System;
using?System.Collections.Generic;
using?System.Windows;
using?System.Windows.Controls;
using?System.Windows.Media;
using?System.Windows.Media.Animation;namespace?WPFDevelopers.Samples.ExampleViews
{///?<summary>///?BarrageExample.xaml?的交互邏輯///?</summary>public?partial?class?BarrageExample?:?UserControl{private?Dictionary<TimeSpan,?List<Border>>?_dicBorder;private?long?_num,?_index;private?double?_right,?_top;private?Random?_random?=?new?Random();public?BarrageExample(){InitializeComponent();_dicBorder?=?new?Dictionary<TimeSpan,?List<Border>>();Loaded?+=?delegate{_num?=?(int)(ActualHeight?-?MyGrid.ActualHeight)?/?40;var?list?=?new?List<string>();list.Add("2333");list.Add("測試彈幕");list.Add("很難開心");list.Add("map");list.Add("map加載");list.Add("bing");list.Add("地圖");foreach?(var?item?in?list){SolidColorBrush?brush?=?new?SolidColorBrush(Color.FromRgb((byte)_random.Next(1,?255),(byte)_random.Next(1,?255),?(byte)_random.Next(1,?233)));AddBarrage(brush.Color,?item);}};}void?AddBarrage(Color?color,?string?text){_index++;TimeSpan?time?=?default;var?linearGradientBrush?=?new?LinearGradientBrush(){StartPoint?=?new?Point(0,?0),EndPoint?=?new?Point(1,?1),MappingMode?=?BrushMappingMode.RelativeToBoundingBox,GradientStops?=?new?GradientStopCollection{new?GradientStop?{?Color?=?Colors.Transparent,?Offset?=?2},new?GradientStop?{?Color?=?color?},},};var?border?=?new?Border(){Background?=?linearGradientBrush,Height?=?40,CornerRadius?=?new?CornerRadius(20),Padding?=?new?Thickness(40,?0,?40,?0)};var?textBlock?=?new?TextBlock(){Text?=?text,Foreground?=?Brushes.White,VerticalAlignment?=?VerticalAlignment.Center,};border.Child?=?textBlock;MyCanvas.Children.Add(border);border.Loaded?+=?delegate{time?=?TimeSpan.FromMilliseconds(border.ActualWidth?*?60);_right?=?_right?==?0???ActualWidth?+?border.ActualWidth?:?_right;var?y?=?ActualHeight?-?MyGrid.ActualHeight?-?border.ActualHeight;_top?=?_top?+?40?>=?y???border.ActualHeight?:?_top;Canvas.SetLeft(border,?_right);Canvas.SetTop(border,?_top);var?doubleAnimation?=?new?DoubleAnimation{From?=?_right,To?=?-(ActualWidth?+?border.ActualWidth),Duration?=?time};doubleAnimation.Completed?+=?(s,?e)?=>{var?animationClock?=?s?as?AnimationClock;if?(animationClock?==?null)?return;var?duration?=?animationClock.Timeline.Duration;var?bordersList?=?new?List<Border>();_dicBorder.TryGetValue(duration.TimeSpan,?out?bordersList);if?(bordersList?!=?null?&&?bordersList.Count?>?0){foreach?(var?item?in?bordersList){MyCanvas.Children.Remove(item);}_dicBorder.Remove(duration.TimeSpan);}};border.BeginAnimation(Canvas.LeftProperty,?doubleAnimation);_top?+=?border.ActualHeight?+?20;if?(!_dicBorder.ContainsKey(time))_dicBorder.Add(time,?new?List<Border>?{?border?});else{var?bordersList?=?new?List<Border>();_dicBorder.TryGetValue(time,?out?bordersList);bordersList.Add(border);}};if?(_index?>?_num){_index?=?0;}}private?void?ButtonBase_OnClick(object?sender,?RoutedEventArgs?e){SolidColorBrush?brush?=?new?SolidColorBrush(Color.FromRgb((byte)_random.Next(1,?255),(byte)_random.Next(1,?255),?(byte)_random.Next(1,?233)));AddBarrage(brush.Color,?tbBarrage.Text);}}}

Github|BarrageExample.xaml.cs[1]
Gitee|BarrageExample.xaml.cs[2]
參考資料
[1]
Github|BarrageExample.xaml.cs: https://github.com/WPFDevelopersOrg/WPFDevelopers/blob/master/src/WPFDevelopers.Samples/ExampleViews/BarrageExample.xaml.cs
[2]Gitee|BarrageExample.xaml.cs: https://gitee.com/WPFDevelopersOrg/WPFDevelopers/blob/master/src/WPFDevelopers.Samples/ExampleViews/BarrageExample.xaml.cs