?SkiaSharp 自繪彈幕效果
控件名:SkiaSharpBarrage
作者: 驚鏵
原文鏈接: ? ?https://github.com/yanjinhuagood/SkiaSharpBarrage
框架使用
.NET60
;Visual Studio 2022
;項目使用 MIT 開源許可協議;
接著上一篇 WPF 彈幕
上期有網友建議使用Skia實現彈幕。
彈幕消息使用
SKElement
做彈幕展現,然后在SKCanvas
進行繪制彈幕。由于需要繪制矩形與文本所以需要使用到
SKBitmap
進行繪制彈幕類。創建
SKBitmap
設置寬(根據文本的長度定義寬度)與高度40
。創建對象
SKCanvas
并實例化的時候將SKBitmap
傳入,然后對SKCanvas
進行繪制背景DrawRoundRect
與文本DrawText
,使用屬性記錄X
與Y
的值方便在動畫的時候讓彈幕動起來。Barrage
在Render
的時候進行繪制彈幕圖片DrawImage(SKBitmap,x,y)
。彈幕每次移動多少值 等于
SKCanvas
的寬除以彈幕的寬
。當彈幕移動
Move()
時如超過-Width
則通過out
返回GUID
就移除彈幕對象。
1) 準備 MsgInfo 彈幕消息類如下:
using?System;
using?SkiaSharp;namespace?SkiaSharpBarrage
{///?<summary>///?????msg?info///?</summary>public?class?MsgInfo{private?string?_msg;public?string?GUID;public?MsgInfo(string?msg,?SKTypeface?_font,?float?windowsWidth){_msg?=?msg;var?_random?=?new?Random();var?skColor?=?new?SKColor((byte)?_random.Next(1,?255),(byte)?_random.Next(1,?255),?(byte)?_random.Next(1,?233));using?var?paint?=?new?SKPaint{Color?=?skColor,Style?=?SKPaintStyle.Fill,IsAntialias?=?true,StrokeWidth?=?2};paint.Shader?=?SKShader.CreateLinearGradient(new?SKPoint(0,?0),new?SKPoint(1,?1),new[]?{SKColors.Transparent,?skColor},new?float[]?{0,?1},SKShaderTileMode.Repeat);using?var?paintText?=?new?SKPaint{Color?=?SKColors.White,IsAntialias?=?true,Typeface?=?_font,TextSize?=?24};var?textBounds?=?new?SKRect();paintText.MeasureText(msg,?ref?textBounds);var?width?=?textBounds.Width?+?100;SKImage?skImage;using?(var?bitmap?=?new?SKBitmap((int)?width,?40,?true))using?(var?canvas?=?new?SKCanvas(bitmap)){canvas.DrawRoundRect(0,?0,?width,?40,?20,?20,?paint);canvas.DrawText(msg,?width?/?2?-?textBounds.Width?/?2,?bitmap.Height?/?2?+?textBounds.Height?/?2,paintText);var?image?=?SKImage.FromBitmap(bitmap);skImage?=?image;}SKImage?=?skImage;Width?=?width;X?=?windowsWidth?+?Width;CanvasWidth?=?windowsWidth;CostTime?=?TimeSpan.FromMilliseconds(Width);GUID?=?Guid.NewGuid().ToString("N");}public?float?X?{?get;?set;?}public?float?Y?{?get;?set;?}public?float?Width?{?get;?set;?}public?float?CanvasWidth?{?get;?set;?}public?SKImage?SKImage?{?get;?set;?}public?float?MoveNum?=>?CanvasWidth?/?(float)?CostTime.TotalMilliseconds;public?TimeSpan?CostTime?{?get;?set;?}///?<summary>///?????定時調用,移動指定距離///?</summary>public?void?Move(out?string?guid){guid?=?string.Empty;X?=?X?-?MoveNum;if?(X?<=?-Width)guid?=?GUID;}}
}
2) 新建 Barrage.cs 類如下:
using?System.Collections.Generic;
using?System.Linq;
using?SkiaSharp;namespace?SkiaSharpBarrage
{public?class?Barrage{private?readonly?SKTypeface?_font;private?readonly?List<MsgInfo>?_MsgInfo;private?int?_num,?_index;private?double?_right,?_top;private?float?_width;private?readonly?float?_height;public?Barrage(SKTypeface?font,?float?width,?float?height,?List<string>?strList){_width?=?width;_height?=?height;_font?=?font;_num?=?(int)?height?/?40;_MsgInfo?=?new?List<MsgInfo>();foreach?(var?item?in?strList)?BuildMsgInfo(item);}private?void?BuildMsgInfo(string?text){_index++;if?(_right?!=?0)_width?=?(float)?_right;var?model?=?new?MsgInfo(text,?_font,?_width);_right?=?_right?==?0???_height?+?model.Width?:?_right;var?y?=?_height?-?40;_top?=?_top?+?40?>=?y???40?:?_top;model.Y?=?(float)?_top;_MsgInfo.Add(model);_top?+=?60;}public?void?AddBarrage(string?text){BuildMsgInfo(text);}public?void?Render(SKCanvas?canvas,?SKTypeface?font,?int?width,?int?height,?List<string>?strList){for?(var?i?=?0;?i?<?_MsgInfo.Count;?i++){var?info?=?_MsgInfo[i];var?guid?=?string.Empty;info.Move(out?guid);if?(!string.IsNullOrEmpty(guid)){var?model?=?_MsgInfo.FirstOrDefault(x?=>?x.GUID?==?guid);_MsgInfo.Remove(model);}canvas.DrawImage(info.SKImage,?info.X,?info.Y);}}}
}
3) MainWindow.xaml.cs 如下:
<wpfdev:Window?x:Class="SkiaSharpBarrage.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers"xmlns:skia="clr-namespace:SkiaSharp.Views.WPF;assembly=SkiaSharp.Views.WPF"mc:Ignorable="d"?WindowStartupLocation="CenterScreen"ResizeMode="CanMinimize"Title="SkiaSharpBarrage?-?彈幕篇"?Height="450"?Width="800"><Grid?Margin="4"><Grid.RowDefinitions><RowDefinition?/><RowDefinition?Height="Auto"?/></Grid.RowDefinitions><MediaElement?Stretch="Uniform"?Grid.RowSpan="2"Name="myMediaElement"?/><skia:SKElement?x:Name="skElement"?/><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>
</wpfdev:Window>
3) 邏輯 MainWindow.xaml.cs 如下:
using?System;
using?System.Collections.Generic;
using?System.IO;
using?System.Linq;
using?System.Threading;
using?System.Threading.Tasks;
using?System.Windows;
using?SkiaSharp;
using?SkiaSharp.Views.Desktop;namespace?SkiaSharpBarrage
{///?<summary>///?????Interaction?logic?for?MainWindow.xaml///?</summary>public?partial?class?MainWindow{private?readonly?Barrage?_barrage;private?readonly?SKTypeface?_font;private?readonly?List<string>?list?=?new?List<string>();public?MainWindow(){list.Add("2333");list.Add("測試彈幕公眾號:WPF開發者");list.Add("很難開心");list.Add("LOL~");list.Add("青春記憶");list.Add("bing");list.Add("Microsoft");InitializeComponent();var?index?=?SKFontManager.Default.FontFamilies.ToList().IndexOf("微軟雅黑");_font?=?SKFontManager.Default.GetFontStyles(index).CreateTypeface(0);_barrage?=?new?Barrage(_font,?(float)?Width,?(float)?Height?-?(float)?MyGrid.ActualHeight,?list);skElement.PaintSurface?+=?SkElement_PaintSurface;Loaded?+=?delegate{myMediaElement.Source?=new?Uri(Path.Combine(AppDomain.CurrentDomain.BaseDirectory,?"Leagueoflegends.mp4"));};_?=?Task.Run(()?=>{try{while?(true){Dispatcher.Invoke(()?=>?{?skElement.InvalidateVisual();?});_?=?SpinWait.SpinUntil(()?=>?false,?1000?/?60);?//每秒60幀}}catch?(Exception?e){}});}private?void?SkElement_PaintSurface(object??sender,?SKPaintSurfaceEventArgs?e){var?canvas?=?e.Surface.Canvas;canvas.Clear();_barrage.Render(canvas,?_font,?e.Info.Width,?e.Info.Height,?list);}private?void?ButtonBase_OnClick(object?sender,?RoutedEventArgs?e){_barrage.AddBarrage(tbBarrage.Text);}}
}

Github|SkiaSharpBarrage[1]
Gitee|SkiaSharpBarrage[2]
參考資料
[1]
Github|SkiaSharpBarrage: https://github.com/yanjinhuagood/SkiaSharpBarrage
[2]Gitee|SkiaSharpBarrage: https://gitee.com/yanjinhua/SkiaSharpBarrage