WPF+SkiaSharp實(shí)現(xiàn)自繪彈幕效果
SkiaSharp 自繪彈幕效果
框架使用.NET60
;
Visual Studio 2022
;
項(xiàng)目使用 MIT 開源許可協(xié)議;
上期有網(wǎng)友建議使用Skia實(shí)現(xiàn)彈幕。
- 彈幕消息使用
SKElement
做彈幕展現(xiàn),然后在SKCanvas
進(jìn)行繪制彈幕。 - 由于需要繪制矩形與文本所以需要使用到
SKBitmap
進(jìn)行繪制彈幕類。 - 創(chuàng)建
SKBitmap
設(shè)置寬(根據(jù)文本的長度定義寬度)與高度40
。 - 創(chuàng)建對(duì)象
SKCanvas
并實(shí)例化的時(shí)候?qū)?code>SKBitmap傳入,然后對(duì)SKCanvas
進(jìn)行繪制背景DrawRoundRect
與文本DrawText
,使用屬性記錄X
與Y
的值方便在動(dòng)畫的時(shí)候讓彈幕動(dòng)起來。 Barrage
在Render
的時(shí)候進(jìn)行繪制彈幕圖片DrawImage(SKBitmap,x,y)
。- 彈幕每次移動(dòng)多少值 等于
SKCanvas
的寬除以彈幕的寬
。 - 當(dāng)彈幕移動(dòng)
Move()
時(shí)如超過-Width
則通過out
返回GUID
就移除彈幕對(duì)象。
實(shí)現(xiàn)代碼
1) 準(zhǔn)備 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> ????????///?????定時(shí)調(diào)用,移動(dòng)指定距離 ????????///?</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="請(qǐng)彈幕內(nèi)容"?/> ????????????<Button?Grid.Column="1"?Style="{StaticResource?PrimaryButton}" ????????????????????Content="發(fā)射彈幕"?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("測(cè)試彈幕公眾號(hào):WPF開發(fā)者"); ????????????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); ????????} ????} }
實(shí)現(xiàn)效果
到此這篇關(guān)于WPF+SkiaSharp實(shí)現(xiàn)自繪彈幕效果的文章就介紹到這了,更多相關(guān)WPF SkiaSharp彈幕內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#后端接收form-data,創(chuàng)建實(shí)體類教程
這篇文章主要介紹了C#后端接收form-data,創(chuàng)建實(shí)體類教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06C#網(wǎng)絡(luò)編程基礎(chǔ)之進(jìn)程和線程詳解
這篇文章主要介紹了C#網(wǎng)絡(luò)編程基礎(chǔ)之進(jìn)程和線程詳解,本文對(duì)進(jìn)程、線程、線程池知識(shí)做了淺顯易懂的講解,并配有代碼實(shí)例,需要的朋友可以參考下2014-08-08Unity編輯器資源導(dǎo)入處理函數(shù)OnPostprocessTexture實(shí)例深入解析
這篇文章主要為大家介紹了Unity編輯器資源導(dǎo)入處理函數(shù)OnPostprocessTexture實(shí)例深入解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09