├── NSDanmaku ├── nuget.exe ├── Controls │ ├── Danmaku.xaml │ ├── TantanDialog.xaml │ ├── TantanDialog.xaml.cs │ ├── DanmakuExtensions.cs │ ├── DanmakuItemControl.cs │ └── Danmaku.xaml.cs ├── Properties │ ├── NSDanmaku.rd.xml │ └── AssemblyInfo.cs ├── Helper │ ├── WebHelper.cs │ ├── Utils.cs │ ├── TanTanPlay.cs │ └── DanmakuParse.cs ├── Model │ ├── TantanModel.cs │ └── DanmakuModel.cs ├── NSDanmaku.nuspec └── NSDanmaku.csproj ├── Demo ├── Assets │ ├── StoreLogo.png │ ├── SplashScreen.scale-200.png │ ├── LockScreenLogo.scale-200.png │ ├── Square44x44Logo.scale-200.png │ ├── Wide310x150Logo.scale-200.png │ ├── Square150x150Logo.scale-200.png │ └── Square44x44Logo.targetsize-24_altform-unplated.png ├── App.xaml ├── Properties │ ├── Default.rd.xml │ └── AssemblyInfo.cs ├── Package.appxmanifest ├── App.xaml.cs ├── MainPage.xaml ├── MainPage.xaml.cs └── Demo.csproj ├── Demo.WinUI ├── Assets │ ├── StoreLogo.png │ ├── SplashScreen.scale-200.png │ ├── LockScreenLogo.scale-200.png │ ├── Square150x150Logo.scale-200.png │ ├── Square44x44Logo.scale-200.png │ ├── Wide310x150Logo.scale-200.png │ └── Square44x44Logo.targetsize-24_altform-unplated.png ├── Properties │ ├── launchSettings.json │ └── PublishProfiles │ │ ├── win-x64.pubxml │ │ ├── win-x86.pubxml │ │ └── win-arm64.pubxml ├── App.xaml ├── app.manifest ├── App.xaml.cs ├── Package.appxmanifest ├── Demo.WinUI.csproj ├── MainWindow.xaml.cs └── MainWindow.xaml ├── NSDanmaku.WinUI ├── Controls │ ├── Danmaku.xaml.cs │ ├── Danmaku.xaml │ ├── DanmakuExtensions.cs │ └── DanmakuItemControl.cs ├── Helper │ ├── WebHelper.cs │ ├── Utils.cs │ └── DanmakuParse.cs ├── NSDanmaku.WinUI.csproj └── Model │ └── DanmakuModel.cs ├── README.md ├── .gitattributes ├── .gitignore └── NSDanmaku.sln /NSDanmaku/nuget.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyaocz/NSDanmaku/HEAD/NSDanmaku/nuget.exe -------------------------------------------------------------------------------- /Demo/Assets/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyaocz/NSDanmaku/HEAD/Demo/Assets/StoreLogo.png -------------------------------------------------------------------------------- /Demo.WinUI/Assets/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyaocz/NSDanmaku/HEAD/Demo.WinUI/Assets/StoreLogo.png -------------------------------------------------------------------------------- /Demo/Assets/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyaocz/NSDanmaku/HEAD/Demo/Assets/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /Demo/Assets/LockScreenLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyaocz/NSDanmaku/HEAD/Demo/Assets/LockScreenLogo.scale-200.png -------------------------------------------------------------------------------- /Demo/Assets/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyaocz/NSDanmaku/HEAD/Demo/Assets/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /Demo/Assets/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyaocz/NSDanmaku/HEAD/Demo/Assets/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /NSDanmaku.WinUI/Controls/Danmaku.xaml.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyaocz/NSDanmaku/HEAD/NSDanmaku.WinUI/Controls/Danmaku.xaml.cs -------------------------------------------------------------------------------- /Demo.WinUI/Assets/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyaocz/NSDanmaku/HEAD/Demo.WinUI/Assets/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /Demo/Assets/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyaocz/NSDanmaku/HEAD/Demo/Assets/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /Demo.WinUI/Assets/LockScreenLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyaocz/NSDanmaku/HEAD/Demo.WinUI/Assets/LockScreenLogo.scale-200.png -------------------------------------------------------------------------------- /Demo.WinUI/Assets/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyaocz/NSDanmaku/HEAD/Demo.WinUI/Assets/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /Demo.WinUI/Assets/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyaocz/NSDanmaku/HEAD/Demo.WinUI/Assets/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /Demo.WinUI/Assets/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyaocz/NSDanmaku/HEAD/Demo.WinUI/Assets/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /Demo/Assets/Square44x44Logo.targetsize-24_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyaocz/NSDanmaku/HEAD/Demo/Assets/Square44x44Logo.targetsize-24_altform-unplated.png -------------------------------------------------------------------------------- /Demo.WinUI/Assets/Square44x44Logo.targetsize-24_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyaocz/NSDanmaku/HEAD/Demo.WinUI/Assets/Square44x44Logo.targetsize-24_altform-unplated.png -------------------------------------------------------------------------------- /Demo.WinUI/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Demo.WinUI (Package)": { 4 | "commandName": "MsixPackage" 5 | }, 6 | "Demo.WinUI (Unpackaged)": { 7 | "commandName": "Project" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /Demo/App.xaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo.WinUI/Properties/PublishProfiles/win-x64.pubxml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | FileSystem 8 | x64 9 | win-x64 10 | bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ 11 | true 12 | False 13 | 14 | -------------------------------------------------------------------------------- /Demo.WinUI/Properties/PublishProfiles/win-x86.pubxml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | FileSystem 8 | x86 9 | win-x86 10 | bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ 11 | true 12 | False 13 | 14 | -------------------------------------------------------------------------------- /Demo.WinUI/Properties/PublishProfiles/win-arm64.pubxml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | FileSystem 8 | ARM64 9 | win-arm64 10 | bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ 11 | true 12 | False 13 | 14 | -------------------------------------------------------------------------------- /Demo.WinUI/App.xaml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /NSDanmaku.WinUI/Controls/Danmaku.xaml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /NSDanmaku/Controls/Danmaku.xaml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /NSDanmaku/Properties/NSDanmaku.rd.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Demo/Properties/Default.rd.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Demo.WinUI/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | PerMonitorV2 17 | 18 | 19 | -------------------------------------------------------------------------------- /NSDanmaku.WinUI/Helper/WebHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Runtime.InteropServices.WindowsRuntime; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | 10 | namespace NSDanmaku.WinUI.Helper 11 | { 12 | class WebHelper 13 | { 14 | public async Task GetResults(Uri url) 15 | { 16 | var handler = new HttpClientHandler() 17 | { 18 | AutomaticDecompression = System.Net.DecompressionMethods.Deflate | System.Net.DecompressionMethods.GZip, 19 | }; 20 | using (HttpClient hc = new HttpClient(handler)) 21 | { 22 | 23 | HttpResponseMessage hr = await hc.GetAsync(url); 24 | hr.EnsureSuccessStatusCode(); 25 | var encodeResults = await hr.Content.ReadAsByteArrayAsync(); 26 | string results = Encoding.Default.GetString(encodeResults); 27 | return results; 28 | } 29 | } 30 | 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /NSDanmaku/Helper/WebHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices.WindowsRuntime; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Windows.Web.Http; 8 | using Windows.Web.Http.Filters; 9 | 10 | namespace NSDanmaku.Helper 11 | { 12 | class WebHelper 13 | { 14 | public async Task GetResults(Uri url) 15 | { 16 | HttpBaseProtocolFilter fiter = new HttpBaseProtocolFilter(); 17 | fiter.IgnorableServerCertificateErrors.Add(Windows.Security.Cryptography.Certificates.ChainValidationResult.Expired); 18 | using (HttpClient hc = new HttpClient(fiter)) 19 | { 20 | HttpResponseMessage hr = await hc.GetAsync(url); 21 | hr.EnsureSuccessStatusCode(); 22 | var encodeResults = await hr.Content.ReadAsBufferAsync(); 23 | string results = Encoding.UTF8.GetString(encodeResults.ToArray(), 0, encodeResults.ToArray().Length); 24 | return results; 25 | } 26 | } 27 | 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Demo/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Demo")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Demo")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("1.0.0.0")] 28 | [assembly: AssemblyFileVersion("1.0.0.0")] 29 | [assembly: ComVisible(false)] -------------------------------------------------------------------------------- /NSDanmaku/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("NSDanmaku")] 9 | [assembly: AssemblyDescription("一个简单的UWP弹幕控件")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("xiaoyaocz")] 12 | [assembly: AssemblyProduct("NSDanmaku")] 13 | [assembly: AssemblyCopyright("Copyright © 2023")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("2.2.0.0")] 28 | [assembly: AssemblyFileVersion("2.2.0.0")] 29 | [assembly: ComVisible(false)] -------------------------------------------------------------------------------- /NSDanmaku/Model/TantanModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace NSDanmaku.Model 8 | { 9 | public class TantanSearchModel 10 | { 11 | public bool success { get; set; } 12 | public string errorMessage { get; set; } 13 | public int errorCode { get; set; } 14 | public bool hasMore { get; set; } 15 | public List animes { get; set; } 16 | } 17 | public class animes 18 | { 19 | public int animeId { get; set; } 20 | public string animeTitle { get; set; } 21 | public int type { get; set; } 22 | public List episodes { get; set; } 23 | } 24 | public class episodes 25 | { 26 | public int episodeId { get; set; } 27 | public string episodeTitle { get; set; } 28 | public string animeTitle { get; set; } 29 | } 30 | 31 | public class CommentModel 32 | { 33 | public int count { get; set; } 34 | public List comments { get; set; } 35 | } 36 | public class comments 37 | { 38 | public long cid { get; set; } 39 | public string m { get; set; } 40 | public string p { get; set; } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /NSDanmaku.WinUI/NSDanmaku.WinUI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | True 4 | 5 | 6 | net8.0-windows10.0.19041.0 7 | 10.0.17763.0 8 | NSDanmaku.WinUI 9 | win-x86;win-x64;win-arm64 10 | true 11 | NSDanmaku.WinUI 12 | xiaoyaocz 13 | xiaoyaocz 14 | 一个简单的WinUI3弹幕控件 15 | https://github.com/xiaoyaocz/NSDanmaku 16 | https://github.com/xiaoyaocz/NSDanmaku 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | MSBuild:Compile 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Demo/Package.appxmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | Demo 18 | xiaoyaocz 19 | Assets\StoreLogo.png 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NSDanmaku 2 | 一个简单的UWP/WinUI3弹幕控件 3 | 4 | ## 截图 5 | ![pic0.png](https://i.loli.net/2021/02/22/taBKgwvI3RymJ8V.png) 6 | 7 | ## 快速入门 8 | 9 | 1、添加nuget引用 10 | 11 | UWP: `Install-Package NSDanmaku` 12 | WinUI3: `Install-Package NSDanmaku.WinUI` 13 | 14 | 2、XAML 15 | ``` 16 | ... 17 | xmlns:controls="using:NSDanmaku.Controls" // UWP 18 | xmlns:controls="using:NSDanmaku.WinUI.Controls" // WinUI3 19 | ... 20 | 21 | 22 | 23 | 24 | ``` 25 | 26 | 3、添加弹幕 27 | ``` 28 | //添加弹幕 29 | danmaku.AddDanmu(...); 30 | //添加滚动弹幕 31 | danmaku.AddRollDanmu(...); 32 | //添加顶部弹幕 33 | danmaku.AddTopDanmu(...); 34 | //添加底部弹幕 35 | danmaku.AddBottomDanmu(...); 36 | ``` 37 | ## 常用方法 38 | 39 | ``` 40 | //暂停弹幕 41 | danmaku.PauseDanmaku(); 42 | //继续弹幕 43 | danmaku.ResumeDanmaku(); 44 | //移除指定的弹幕 45 | danmaku.Remove(...); 46 | //移除屏幕上的弹幕 47 | danmaku.ClearAll(...); 48 | //读取屏幕上的弹幕 49 | danmaku.GetDanmakus(...); 50 | //隐藏指定位置的弹幕 51 | danmaku.HideDanmaku(...); 52 | //显示指定位置的弹幕 53 | danmaku.ShowDanmaku(...); 54 | ``` 55 | 56 | 57 | ## 属性 58 | - `DanmakuBold` 弹幕是否加粗 59 | 60 | - `DanmakuSizeZoom` 字体大小缩放倍数 61 | 62 | - `DanmakuDuration` 滚动弹幕动画持续时间,单位:秒,越小弹幕移动速度越快 63 | 64 | - `DanmakuFontFamily` 弹幕字体名称 65 | 66 | - `DanmakuStyle` 弹幕样式,可选以下三种样式: 67 | - Overlap:两个TextBlock重叠,模拟描边 68 | - WithoutStroke:无描边,单TextBlock 69 | - Stroke:描边 70 | 71 | ## 2.1.0修改 72 | - 移除Microsoft.Toolkit.Uwp.UI.Controls 73 | 74 | - 移除DanmakuStyle命名,移除Shadow样式 75 | 76 | - 优化描边弹幕DPI 77 | 78 | - 防止弹幕超出边界 79 | 80 | - 升级引用的包版本 81 | 82 | ## 参考资料 83 | [https://www.zhihu.com/question/370464345](https://www.zhihu.com/question/370464345) 84 | 85 | [https://zhuanlan.zhihu.com/p/159027974](https://zhuanlan.zhihu.com/p/159027974) 86 | -------------------------------------------------------------------------------- /Demo.WinUI/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml; 2 | using Microsoft.UI.Xaml.Controls; 3 | using Microsoft.UI.Xaml.Controls.Primitives; 4 | using Microsoft.UI.Xaml.Data; 5 | using Microsoft.UI.Xaml.Input; 6 | using Microsoft.UI.Xaml.Media; 7 | using Microsoft.UI.Xaml.Navigation; 8 | using Microsoft.UI.Xaml.Shapes; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.IO; 12 | using System.Linq; 13 | using System.Runtime.InteropServices.WindowsRuntime; 14 | using Windows.ApplicationModel; 15 | using Windows.ApplicationModel.Activation; 16 | using Windows.Foundation; 17 | using Windows.Foundation.Collections; 18 | 19 | // To learn more about WinUI, the WinUI project structure, 20 | // and more about our project templates, see: http://aka.ms/winui-project-info. 21 | 22 | namespace Demo.WinUI 23 | { 24 | /// 25 | /// Provides application-specific behavior to supplement the default Application class. 26 | /// 27 | public partial class App : Application 28 | { 29 | private Window? _window; 30 | 31 | /// 32 | /// Initializes the singleton application object. This is the first line of authored code 33 | /// executed, and as such is the logical equivalent of main() or WinMain(). 34 | /// 35 | public App() 36 | { 37 | InitializeComponent(); 38 | } 39 | 40 | /// 41 | /// Invoked when the application is launched. 42 | /// 43 | /// Details about the launch request and process. 44 | protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) 45 | { 46 | _window = new MainWindow(); 47 | _window.Activate(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /NSDanmaku/NSDanmaku.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NSDanmaku 5 | 2.2.0 6 | NSDanmaku 7 | xiaoyaocz 8 | xiaoyaocz 9 | https://github.com/xiaoyaocz/NSDanmaku 10 | false 11 | 一个简单的UWP弹幕控件 12 | Copyright 2023 13 | UWP 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Demo.WinUI/Package.appxmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | Demo.WinUI 19 | xiaoyaocz 20 | Assets\StoreLogo.png 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 36 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /NSDanmaku/Controls/TantanDialog.xaml: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /NSDanmaku/Controls/TantanDialog.xaml.cs: -------------------------------------------------------------------------------- 1 | using NSDanmaku.Helper; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.InteropServices.WindowsRuntime; 7 | using Windows.Foundation; 8 | using Windows.Foundation.Collections; 9 | using Windows.UI.Xaml; 10 | using Windows.UI.Xaml.Controls; 11 | using Windows.UI.Xaml.Controls.Primitives; 12 | using Windows.UI.Xaml.Data; 13 | using Windows.UI.Xaml.Input; 14 | using Windows.UI.Xaml.Media; 15 | using Windows.UI.Xaml.Navigation; 16 | 17 | // https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“内容对话框”项模板 18 | 19 | namespace NSDanmaku.Controls 20 | { 21 | public sealed partial class TantanDialog : ContentDialog 22 | { 23 | TanTanPlay tantan; 24 | public TantanDialog() 25 | { 26 | this.InitializeComponent(); 27 | tantan = new TanTanPlay(); 28 | } 29 | public event EventHandler> ReturnDanmakus; 30 | private async void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) 31 | { 32 | if (list_Items.SelectedItem==null) 33 | { 34 | args.Cancel = true; 35 | return; 36 | } 37 | var data = list_Items.SelectedItem as Model.episodes; 38 | ReturnDanmakus(null,await tantan.GetDanmakus(data.episodeId)); 39 | } 40 | 41 | private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) 42 | { 43 | } 44 | 45 | private async void txt_search_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args) 46 | { 47 | txt_error.Visibility = Visibility.Collapsed; 48 | if (txt_search.Text.Length==0) 49 | { 50 | ShowError("请输入关键字"); 51 | return; 52 | } 53 | try 54 | { 55 | list_Items.ItemsSource = await tantan.Search(txt_search.Text); 56 | } 57 | catch (Exception ex) 58 | { 59 | ShowError(ex.Message); 60 | } 61 | } 62 | 63 | private void ShowError(string msg) 64 | { 65 | txt_error.Visibility = Visibility.Visible ; 66 | txt_error.Text = msg; 67 | } 68 | 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /NSDanmaku/Helper/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Windows.UI; 7 | 8 | namespace NSDanmaku 9 | { 10 | public static class Utils 11 | { 12 | public static int ToInt32(this object obj) 13 | { 14 | 15 | if (int.TryParse(obj?.ToString(), out var value)) 16 | { 17 | return value; 18 | } 19 | else 20 | { 21 | return 0; 22 | } 23 | } 24 | public static double ToDouble(this object obj) 25 | { 26 | if (double.TryParse(obj?.ToString(), out var value)) 27 | { 28 | return value; 29 | } 30 | else 31 | { 32 | return 0d; 33 | } 34 | } 35 | 36 | public static Color ToColor(this string obj) 37 | { 38 | 39 | obj = obj.Replace("#", ""); 40 | obj = Convert.ToInt32(obj).ToString("X2"); 41 | 42 | Color color = new Color(); 43 | if (obj.Length==4) 44 | { 45 | obj = "00" + obj; 46 | } 47 | if (obj.Length == 6) 48 | { 49 | color.R = byte.Parse(obj.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); 50 | color.G = byte.Parse(obj.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); 51 | color.B = byte.Parse(obj.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); 52 | color.A = 255; 53 | } 54 | if (obj.Length == 8) 55 | { 56 | color.R = byte.Parse(obj.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); 57 | color.G = byte.Parse(obj.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); 58 | color.B = byte.Parse(obj.Substring(6, 2), System.Globalization.NumberStyles.HexNumber); 59 | color.A = byte.Parse(obj.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); 60 | } 61 | 62 | return color; 63 | } 64 | 65 | public static string DecodeHTML(this string obj) 66 | { 67 | obj = System.Net.WebUtility.HtmlDecode(obj); 68 | 69 | return obj; 70 | } 71 | /// 72 | /// 读取系统字体列表 73 | /// 74 | /// 75 | public static List GetSystemFontFamilies() 76 | { 77 | string[] fonts = Microsoft.Graphics.Canvas.Text.CanvasTextFormat.GetSystemFontFamilies(); 78 | return fonts.ToList(); 79 | } 80 | 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /NSDanmaku.WinUI/Helper/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Windows.UI; 7 | 8 | namespace NSDanmaku.WinUI 9 | { 10 | public static class Utils 11 | { 12 | public static int ToInt32(this object obj) 13 | { 14 | 15 | if (int.TryParse(obj?.ToString(), out var value)) 16 | { 17 | return value; 18 | } 19 | else 20 | { 21 | return 0; 22 | } 23 | } 24 | public static double ToDouble(this object obj) 25 | { 26 | if (double.TryParse(obj?.ToString(), out var value)) 27 | { 28 | return value; 29 | } 30 | else 31 | { 32 | return 0d; 33 | } 34 | } 35 | 36 | public static Color ToColor(this string obj) 37 | { 38 | 39 | obj = obj.Replace("#", ""); 40 | obj = Convert.ToInt32(obj).ToString("X2"); 41 | 42 | Color color = new Color(); 43 | if (obj.Length==4) 44 | { 45 | obj = "00" + obj; 46 | } 47 | if (obj.Length == 6) 48 | { 49 | color.R = byte.Parse(obj.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); 50 | color.G = byte.Parse(obj.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); 51 | color.B = byte.Parse(obj.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); 52 | color.A = 255; 53 | } 54 | if (obj.Length == 8) 55 | { 56 | color.R = byte.Parse(obj.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); 57 | color.G = byte.Parse(obj.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); 58 | color.B = byte.Parse(obj.Substring(6, 2), System.Globalization.NumberStyles.HexNumber); 59 | color.A = byte.Parse(obj.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); 60 | } 61 | 62 | return color; 63 | } 64 | 65 | public static string DecodeHTML(this string obj) 66 | { 67 | obj = System.Net.WebUtility.HtmlDecode(obj); 68 | 69 | return obj; 70 | } 71 | /// 72 | /// 读取系统字体列表 73 | /// 74 | /// 75 | public static List GetSystemFontFamilies() 76 | { 77 | string[] fonts = Microsoft.Graphics.Canvas.Text.CanvasTextFormat.GetSystemFontFamilies(); 78 | return fonts.ToList(); 79 | } 80 | 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Demo.WinUI/Demo.WinUI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | WinExe 4 | net8.0-windows10.0.19041.0 5 | 10.0.17763.0 6 | Demo.WinUI 7 | app.manifest 8 | x86;x64;ARM64 9 | win-x86;win-x64;win-arm64 10 | win-$(Platform).pubxml 11 | true 12 | true 13 | enable 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 51 | 52 | true 53 | 54 | 55 | 56 | 57 | False 58 | True 59 | False 60 | True 61 | 62 | -------------------------------------------------------------------------------- /NSDanmaku/Model/DanmakuModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Windows.UI; 7 | using Windows.UI.Xaml.Media; 8 | 9 | namespace NSDanmaku.Model 10 | { 11 | public enum DanmakuLocation 12 | { 13 | /// 14 | /// 滚动弹幕Model1-3 15 | /// 16 | Scroll, 17 | /// 18 | /// 顶部弹幕Model5 19 | /// 20 | Top, 21 | /// 22 | /// 底部弹幕Model4 23 | /// 24 | Bottom, 25 | /// 26 | /// 定位弹幕Model7 27 | /// 28 | Position, 29 | /// 30 | /// 其它暂未支持的类型 31 | /// 32 | Other 33 | } 34 | public enum DanmakuSite 35 | { 36 | Bilibili, 37 | Acfun, 38 | Tantan 39 | } 40 | public enum DanmakuBorderStyle 41 | { 42 | /// 43 | /// 重叠描边 44 | /// 45 | Overlap = 0, 46 | /// 47 | /// 无描边 48 | /// 49 | WithoutStroke = 1, 50 | /// 51 | /// 正常描边 52 | /// 53 | Stroke= 2 54 | } 55 | public enum DanmakuMode 56 | { 57 | Video, 58 | Live 59 | } 60 | public class DanmakuModel 61 | { 62 | public string text { get; set; } 63 | /// 64 | /// 弹幕大小 65 | /// 66 | public double size { get; set; } 67 | /// 68 | /// 弹幕颜色 69 | /// 70 | public Color color { get; set; } 71 | /// 72 | /// 弹幕出现时间 73 | /// 74 | public double time { get; set; } 75 | 76 | /// 77 | /// 弹幕出现时间 78 | /// 79 | public int time_s{ get; set; } 80 | /// 81 | /// 弹幕发送时间 82 | /// 83 | public string sendTime { get; set; } 84 | /// 85 | /// 弹幕池 86 | /// 87 | public string pool { get; set; } 88 | /// 89 | /// 弹幕发送人ID 90 | /// 91 | public string sendID { get; set; } 92 | /// 93 | /// 弹幕ID 94 | /// 95 | public string rowID { get; set; } 96 | /// 97 | /// 弹幕出现位置 98 | /// 99 | public DanmakuLocation location 100 | { 101 | get; set; 102 | } 103 | 104 | public DanmakuSite fromSite { get; set; } 105 | 106 | public string source { get; set; } 107 | /// 108 | /// 弹幕权重 109 | /// 110 | public int weight { get; set; } 111 | public SolidColorBrush colorBrush 112 | { 113 | get 114 | { 115 | return new SolidColorBrush(color); 116 | } 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /NSDanmaku.WinUI/Model/DanmakuModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml.Media; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Windows.UI; 8 | 9 | namespace NSDanmaku.Model 10 | { 11 | public enum DanmakuLocation 12 | { 13 | /// 14 | /// 滚动弹幕Model1-3 15 | /// 16 | Scroll, 17 | /// 18 | /// 顶部弹幕Model5 19 | /// 20 | Top, 21 | /// 22 | /// 底部弹幕Model4 23 | /// 24 | Bottom, 25 | /// 26 | /// 定位弹幕Model7 27 | /// 28 | Position, 29 | /// 30 | /// 其它暂未支持的类型 31 | /// 32 | Other 33 | } 34 | public enum DanmakuSite 35 | { 36 | Bilibili, 37 | Acfun, 38 | Tantan 39 | } 40 | public enum DanmakuBorderStyle 41 | { 42 | /// 43 | /// 重叠描边 44 | /// 45 | Overlap = 0, 46 | /// 47 | /// 无描边 48 | /// 49 | WithoutStroke = 1, 50 | /// 51 | /// 正常描边 52 | /// 53 | Stroke= 2 54 | } 55 | public enum DanmakuMode 56 | { 57 | Video, 58 | Live 59 | } 60 | public class DanmakuModel 61 | { 62 | public string text { get; set; } 63 | /// 64 | /// 弹幕大小 65 | /// 66 | public double size { get; set; } 67 | /// 68 | /// 弹幕颜色 69 | /// 70 | public Color color { get; set; } 71 | /// 72 | /// 弹幕出现时间 73 | /// 74 | public double time { get; set; } 75 | 76 | /// 77 | /// 弹幕出现时间 78 | /// 79 | public int time_s{ get; set; } 80 | /// 81 | /// 弹幕发送时间 82 | /// 83 | public string sendTime { get; set; } 84 | /// 85 | /// 弹幕池 86 | /// 87 | public string pool { get; set; } 88 | /// 89 | /// 弹幕发送人ID 90 | /// 91 | public string sendID { get; set; } 92 | /// 93 | /// 弹幕ID 94 | /// 95 | public string rowID { get; set; } 96 | /// 97 | /// 弹幕出现位置 98 | /// 99 | public DanmakuLocation location 100 | { 101 | get; set; 102 | } 103 | 104 | public DanmakuSite fromSite { get; set; } 105 | 106 | public string source { get; set; } 107 | /// 108 | /// 弹幕权重 109 | /// 110 | public int weight { get; set; } 111 | public SolidColorBrush colorBrush 112 | { 113 | get 114 | { 115 | return new SolidColorBrush(color); 116 | } 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Demo/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.InteropServices.WindowsRuntime; 6 | using Windows.ApplicationModel; 7 | using Windows.ApplicationModel.Activation; 8 | using Windows.Foundation; 9 | using Windows.Foundation.Collections; 10 | using Windows.UI.Xaml; 11 | using Windows.UI.Xaml.Controls; 12 | using Windows.UI.Xaml.Controls.Primitives; 13 | using Windows.UI.Xaml.Data; 14 | using Windows.UI.Xaml.Input; 15 | using Windows.UI.Xaml.Media; 16 | using Windows.UI.Xaml.Navigation; 17 | 18 | namespace Demo 19 | { 20 | /// 21 | /// 提供特定于应用程序的行为,以补充默认的应用程序类。 22 | /// 23 | sealed partial class App : Application 24 | { 25 | /// 26 | /// 初始化单一实例应用程序对象。这是执行的创作代码的第一行, 27 | /// 已执行,逻辑上等同于 main() 或 WinMain()。 28 | /// 29 | public App() 30 | { 31 | this.InitializeComponent(); 32 | this.Suspending += OnSuspending; 33 | } 34 | 35 | /// 36 | /// 在应用程序由最终用户正常启动时进行调用。 37 | /// 将在启动应用程序以打开特定文件等情况下使用。 38 | /// 39 | /// 有关启动请求和过程的详细信息。 40 | protected override void OnLaunched(LaunchActivatedEventArgs e) 41 | { 42 | NSDanmaku.Controls.Danmaku.InitDanmakuDpi(); 43 | Frame rootFrame = Window.Current.Content as Frame; 44 | 45 | // 不要在窗口已包含内容时重复应用程序初始化, 46 | // 只需确保窗口处于活动状态 47 | if (rootFrame == null) 48 | { 49 | // 创建要充当导航上下文的框架,并导航到第一页 50 | rootFrame = new Frame(); 51 | 52 | rootFrame.NavigationFailed += OnNavigationFailed; 53 | 54 | if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) 55 | { 56 | //TODO: 从之前挂起的应用程序加载状态 57 | } 58 | 59 | // 将框架放在当前窗口中 60 | Window.Current.Content = rootFrame; 61 | } 62 | 63 | if (e.PrelaunchActivated == false) 64 | { 65 | if (rootFrame.Content == null) 66 | { 67 | // 当导航堆栈尚未还原时,导航到第一页, 68 | // 并通过将所需信息作为导航参数传入来配置 69 | // 参数 70 | rootFrame.Navigate(typeof(MainPage), e.Arguments); 71 | } 72 | // 确保当前窗口处于活动状态 73 | Window.Current.Activate(); 74 | } 75 | } 76 | 77 | /// 78 | /// 导航到特定页失败时调用 79 | /// 80 | ///导航失败的框架 81 | ///有关导航失败的详细信息 82 | void OnNavigationFailed(object sender, NavigationFailedEventArgs e) 83 | { 84 | throw new Exception("Failed to load Page " + e.SourcePageType.FullName); 85 | } 86 | 87 | /// 88 | /// 在将要挂起应用程序执行时调用。 在不知道应用程序 89 | /// 无需知道应用程序会被终止还是会恢复, 90 | /// 并让内存内容保持不变。 91 | /// 92 | /// 挂起的请求的源。 93 | /// 有关挂起请求的详细信息。 94 | private void OnSuspending(object sender, SuspendingEventArgs e) 95 | { 96 | var deferral = e.SuspendingOperation.GetDeferral(); 97 | //TODO: 保存应用程序状态并停止任何后台活动 98 | deferral.Complete(); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Demo/MainPage.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 自己发送的 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 模拟播放 31 | 32 | 33 | 34 | 35 | / 36 | 37 | 38 | 39 | 隐藏滚动弹幕 40 | 显示区域 41 | 42 | 大小缩放 43 | 44 | 透明度 45 | 46 | 滚动速度 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /NSDanmaku/Helper/TanTanPlay.cs: -------------------------------------------------------------------------------- 1 | using NSDanmaku.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Newtonsoft.Json; 8 | 9 | namespace NSDanmaku.Helper 10 | { 11 | /// 12 | /// 弹弹Play相关API操作 13 | /// 14 | public class TanTanPlay 15 | { 16 | public TanTanPlay() 17 | { 18 | webHelper = new WebHelper(); 19 | } 20 | WebHelper webHelper; 21 | /// 22 | /// 通过关键字搜索弹弹Play上的剧集 23 | /// 24 | /// 25 | /// 26 | public async Task> Search(string keyword) 27 | { 28 | 29 | var results = await webHelper.GetResults(new Uri("https://api.dandanplay.net/api/v2/search/episodes?anime=" + Uri.EscapeDataString(keyword))); 30 | var m = JsonConvert.DeserializeObject(results); 31 | if (m.success) 32 | { 33 | List episodes = new List(); 34 | foreach (var item in m.animes) 35 | { 36 | foreach (var item1 in item.episodes) 37 | { 38 | item1.animeTitle = item.animeTitle; 39 | episodes.Add(item1); 40 | } 41 | } 42 | return episodes; 43 | } 44 | else 45 | { 46 | throw new Exception(m.errorMessage); 47 | } 48 | 49 | } 50 | 51 | 52 | public async Task> GetDanmakus(int episodeId) 53 | { 54 | try 55 | { 56 | var results = await webHelper.GetResults(new Uri("https://api.dandanplay.net/api/v2/comment/" + episodeId)); 57 | var m = JsonConvert.DeserializeObject(results); 58 | List list = new List(); 59 | if (m.comments != null) 60 | { 61 | foreach (var item in m.comments) 62 | { 63 | var datas = item.p.Split(','); 64 | var location = DanmakuLocation.Scroll; 65 | switch (datas[1]) 66 | { 67 | case "7": 68 | location = DanmakuLocation.Position; 69 | break; 70 | case "4": 71 | location = DanmakuLocation.Bottom; 72 | break; 73 | case "5": 74 | location = DanmakuLocation.Top; 75 | break; 76 | default: 77 | location = DanmakuLocation.Scroll; 78 | break; 79 | } 80 | list.Add(new DanmakuModel() 81 | { 82 | text = item.m, 83 | color = datas[2].ToColor(), 84 | location = location, 85 | fromSite = DanmakuSite.Tantan, 86 | rowID = item.cid.ToString(), 87 | time = Convert.ToDouble(datas[0]), 88 | sendID = datas[3], 89 | size=25 90 | }); 91 | } 92 | return list; 93 | } 94 | else 95 | { 96 | return list; 97 | } 98 | } 99 | catch (Exception) 100 | { 101 | 102 | return new List(); 103 | } 104 | 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /NSDanmaku/Controls/DanmakuExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Windows.Foundation; 7 | using Windows.UI.Xaml; 8 | 9 | namespace NSDanmaku.Controls 10 | { 11 | /// 12 | /// 防止弹幕超出边界 13 | /// https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/rel/7.1.0/Microsoft.Toolkit.Uwp.UI/Extensions/UIElementExtensions.cs 14 | /// 15 | public static class DanmakuExtensions 16 | { 17 | /// 18 | /// Attached that indicates whether or not the contents of the target should always be clipped to their parent's bounds. 19 | /// 20 | public static readonly DependencyProperty ClipToBoundsProperty = DependencyProperty.RegisterAttached( 21 | "ClipToBounds", 22 | typeof(bool), 23 | typeof(DanmakuExtensions), 24 | new PropertyMetadata(null, OnClipToBoundsPropertyChanged)); 25 | 26 | /// 27 | /// Gets the value of 28 | /// 29 | /// The to read the property value from 30 | /// The associated with the . 31 | public static bool GetClipToBounds(UIElement element) => (bool)element.GetValue(ClipToBoundsProperty); 32 | 33 | /// 34 | /// Sets the value of 35 | /// 36 | /// The to set the property to 37 | /// The new value of the attached property 38 | public static void SetClipToBounds(UIElement element, bool value) => element.SetValue(ClipToBoundsProperty, value); 39 | 40 | private static void OnClipToBoundsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 41 | { 42 | if (d is UIElement element) 43 | { 44 | var clipToBounds = (bool)e.NewValue; 45 | var visual = Windows.UI.Xaml.Hosting.ElementCompositionPreview.GetElementVisual(element); 46 | visual.Clip = clipToBounds ? visual.Compositor.CreateInsetClip() : null; 47 | } 48 | } 49 | 50 | /// 51 | /// Provides the distance in a from the passed in element to the element being called on. 52 | /// For instance, calling child.CoordinatesFrom(container) will return the position of the child within the container. 53 | /// Helper for . 54 | /// 55 | /// Element to measure distance. 56 | /// Starting parent element to provide coordinates from. 57 | /// containing difference in position of elements. 58 | public static Point CoordinatesFrom(this UIElement target, UIElement parent) 59 | { 60 | return target.TransformToVisual(parent).TransformPoint(default(Point)); 61 | } 62 | 63 | /// 64 | /// Provides the distance in a to the passed in element from the element being called on. 65 | /// For instance, calling container.CoordinatesTo(child) will return the position of the child within the container. 66 | /// Helper for . 67 | /// 68 | /// Starting parent element to provide coordinates from. 69 | /// Element to measure distance to. 70 | /// containing difference in position of elements. 71 | public static Point CoordinatesTo(this UIElement parent, UIElement target) 72 | { 73 | return target.TransformToVisual(parent).TransformPoint(default(Point)); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /NSDanmaku.WinUI/Controls/DanmakuExtensions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | // Source:https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/winui/CommunityToolkit.WinUI.UI/Extensions/UIElementExtensions.cs 6 | 7 | using Microsoft.UI.Xaml; 8 | using Microsoft.UI.Xaml.Hosting; 9 | using Point = Windows.Foundation.Point; 10 | 11 | namespace NSDanmaku.WinUI.Controls 12 | { 13 | /// 14 | /// Provides attached dependency properties for the 15 | /// 16 | public static class DanmakuExtensions 17 | { 18 | /// 19 | /// Attached that indicates whether or not the contents of the target should always be clipped to their parent's bounds. 20 | /// 21 | public static readonly DependencyProperty ClipToBoundsProperty = DependencyProperty.RegisterAttached( 22 | "ClipToBounds", 23 | typeof(bool), 24 | typeof(DanmakuExtensions), 25 | new PropertyMetadata(null, OnClipToBoundsPropertyChanged)); 26 | 27 | /// 28 | /// Gets the value of 29 | /// 30 | /// The to read the property value from 31 | /// The associated with the . 32 | public static bool GetClipToBounds(UIElement element) => (bool)element.GetValue(ClipToBoundsProperty); 33 | 34 | /// 35 | /// Sets the value of 36 | /// 37 | /// The to set the property to 38 | /// The new value of the attached property 39 | public static void SetClipToBounds(UIElement element, bool value) => element.SetValue(ClipToBoundsProperty, value); 40 | 41 | private static void OnClipToBoundsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 42 | { 43 | if (d is UIElement element) 44 | { 45 | var clipToBounds = (bool)e.NewValue; 46 | var visual = ElementCompositionPreview.GetElementVisual(element); 47 | visual.Clip = clipToBounds ? visual.Compositor.CreateInsetClip() : null; 48 | } 49 | } 50 | 51 | /// 52 | /// Provides the distance in a from the passed in element to the element being called on. 53 | /// For instance, calling child.CoordinatesFrom(container) will return the position of the child within the container. 54 | /// Helper for . 55 | /// 56 | /// Element to measure distance. 57 | /// Starting parent element to provide coordinates from. 58 | /// containing difference in position of elements. 59 | public static Point CoordinatesFrom(this UIElement target, UIElement parent) 60 | { 61 | return target.TransformToVisual(parent).TransformPoint(default(Point)); 62 | } 63 | 64 | /// 65 | /// Provides the distance in a to the passed in element from the element being called on. 66 | /// For instance, calling container.CoordinatesTo(child) will return the position of the child within the container. 67 | /// Helper for . 68 | /// 69 | /// Starting parent element to provide coordinates from. 70 | /// Element to measure distance to. 71 | /// containing difference in position of elements. 72 | public static Point CoordinatesTo(this UIElement parent, UIElement target) 73 | { 74 | return target.TransformToVisual(parent).TransformPoint(default(Point)); 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /NSDanmaku/Helper/DanmakuParse.cs: -------------------------------------------------------------------------------- 1 | using NSDanmaku.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Text.RegularExpressions; 8 | using System.Threading.Tasks; 9 | using System.Xml; 10 | using Windows.Storage; 11 | 12 | namespace NSDanmaku.Helper 13 | { 14 | public class DanmakuParse 15 | { 16 | public async Task> ParseBiliBili(long cid) 17 | { 18 | WebHelper webHelper = new WebHelper(); 19 | string danmuStr = await webHelper.GetResults(new Uri(string.Format("https://api.bilibili.com/x/v1/dm/list.so?oid={0}", cid))); 20 | return ParseBiliBiliXml(danmuStr); 21 | } 22 | 23 | public async Task GetBiliBili(long cid) 24 | { 25 | 26 | WebHelper webHelper = new WebHelper(); 27 | string danmuStr = await webHelper.GetResults(new Uri(string.Format("https://api.bilibili.com/x/v1/dm/list.so?oid={0}", cid))); 28 | return danmuStr; 29 | } 30 | 31 | 32 | public List ParseBiliBili(string xml) 33 | { 34 | return ParseBiliBiliXml(xml); 35 | } 36 | public async Task> ParseBiliBili(Uri url) 37 | { 38 | WebHelper webHelper = new WebHelper(); 39 | string danmuStr = await webHelper.GetResults(url); 40 | return ParseBiliBiliXml(danmuStr); 41 | } 42 | public async Task< List> ParseBiliBili(Windows.Storage.StorageFile file) 43 | { 44 | string danmuStr = await FileIO.ReadTextAsync(file); 45 | return ParseBiliBiliXml(danmuStr); 46 | } 47 | private List ParseBiliBiliXml(string xmlStr) 48 | { 49 | List ls = new List(); 50 | 51 | XmlDocument xdoc = new XmlDocument(); 52 | //处理下特殊字符 53 | xmlStr = Regex.Replace(xmlStr, @"[\x00-\x08]|[\x0B-\x0C]|[\x0E-\x1F]|[\uFFFF]", ""); 54 | xdoc.LoadXml(xmlStr); 55 | XmlElement el = xdoc.DocumentElement; 56 | XmlNodeList xml = el.ChildNodes; 57 | foreach (XmlNode item in xml) 58 | { 59 | if (item.Attributes["p"] != null) 60 | { 61 | try 62 | { 63 | string text = item.Attributes["p"].Value; 64 | string[] txtSplit = text.Split(','); 65 | var location = DanmakuLocation.Scroll; 66 | switch (txtSplit[1]) 67 | { 68 | case "7": 69 | location = DanmakuLocation.Position; 70 | break; 71 | case "4": 72 | location = DanmakuLocation.Bottom; 73 | break; 74 | case "5": 75 | location = DanmakuLocation.Top; 76 | break; 77 | default: 78 | location = DanmakuLocation.Scroll; 79 | break; 80 | } 81 | ls.Add(new DanmakuModel 82 | { 83 | time = double.Parse(txtSplit[0]), 84 | time_s = Convert.ToInt32(double.Parse(txtSplit[0])), 85 | location = location, 86 | size = double.Parse(txtSplit[2]), 87 | color = txtSplit[3].ToColor(), 88 | sendTime = txtSplit[4], 89 | pool = txtSplit[5], 90 | sendID = txtSplit[6], 91 | rowID = txtSplit[7], 92 | text = item.InnerText, 93 | source = item.OuterXml, 94 | fromSite = DanmakuSite.Bilibili, 95 | //xml弹幕不返回此字段 96 | weight=99 97 | }); 98 | } 99 | catch (Exception ex) 100 | { 101 | Debug.WriteLine(ex.Message); 102 | continue; 103 | } 104 | 105 | } 106 | } 107 | return ls; 108 | 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /NSDanmaku.WinUI/Helper/DanmakuParse.cs: -------------------------------------------------------------------------------- 1 | using NSDanmaku.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Text.RegularExpressions; 8 | using System.Threading.Tasks; 9 | using System.Xml; 10 | using Windows.Storage; 11 | 12 | namespace NSDanmaku.WinUI.Helper 13 | { 14 | public class DanmakuParse 15 | { 16 | public async Task> ParseBiliBili(long cid) 17 | { 18 | WebHelper webHelper = new WebHelper(); 19 | string danmuStr = await webHelper.GetResults(new Uri(string.Format("https://api.bilibili.com/x/v1/dm/list.so?oid={0}", cid))); 20 | return ParseBiliBiliXml(danmuStr); 21 | } 22 | 23 | public async Task GetBiliBili(long cid) 24 | { 25 | 26 | WebHelper webHelper = new WebHelper(); 27 | string danmuStr = await webHelper.GetResults(new Uri(string.Format("https://api.bilibili.com/x/v1/dm/list.so?oid={0}", cid))); 28 | return danmuStr; 29 | } 30 | 31 | 32 | public List ParseBiliBili(string xml) 33 | { 34 | return ParseBiliBiliXml(xml); 35 | } 36 | public async Task> ParseBiliBili(Uri url) 37 | { 38 | WebHelper webHelper = new WebHelper(); 39 | string danmuStr = await webHelper.GetResults(url); 40 | return ParseBiliBiliXml(danmuStr); 41 | } 42 | public async Task< List> ParseBiliBili(Windows.Storage.StorageFile file) 43 | { 44 | string danmuStr = await FileIO.ReadTextAsync(file); 45 | return ParseBiliBiliXml(danmuStr); 46 | } 47 | private List ParseBiliBiliXml(string xmlStr) 48 | { 49 | List ls = new List(); 50 | 51 | XmlDocument xdoc = new XmlDocument(); 52 | //处理下特殊字符 53 | xmlStr = Regex.Replace(xmlStr, @"[\x00-\x08]|[\x0B-\x0C]|[\x0E-\x1F]|[\uFFFF]", ""); 54 | xdoc.LoadXml(xmlStr); 55 | XmlElement el = xdoc.DocumentElement; 56 | XmlNodeList xml = el.ChildNodes; 57 | foreach (XmlNode item in xml) 58 | { 59 | if (item.Attributes["p"] != null) 60 | { 61 | try 62 | { 63 | string text = item.Attributes["p"].Value; 64 | string[] txtSplit = text.Split(','); 65 | var location = DanmakuLocation.Scroll; 66 | switch (txtSplit[1]) 67 | { 68 | case "7": 69 | location = DanmakuLocation.Position; 70 | break; 71 | case "4": 72 | location = DanmakuLocation.Bottom; 73 | break; 74 | case "5": 75 | location = DanmakuLocation.Top; 76 | break; 77 | default: 78 | location = DanmakuLocation.Scroll; 79 | break; 80 | } 81 | ls.Add(new DanmakuModel 82 | { 83 | time = double.Parse(txtSplit[0]), 84 | time_s = Convert.ToInt32(double.Parse(txtSplit[0])), 85 | location = location, 86 | size = double.Parse(txtSplit[2]), 87 | color = txtSplit[3].ToColor(), 88 | sendTime = txtSplit[4], 89 | pool = txtSplit[5], 90 | sendID = txtSplit[6], 91 | rowID = txtSplit[7], 92 | text = item.InnerText, 93 | source = item.OuterXml, 94 | fromSite = DanmakuSite.Bilibili, 95 | //xml弹幕不返回此字段 96 | weight=99 97 | }); 98 | } 99 | catch (Exception ex) 100 | { 101 | Debug.WriteLine(ex.Message); 102 | continue; 103 | } 104 | 105 | } 106 | } 107 | return ls; 108 | 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Demo.WinUI/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI; 2 | using Microsoft.UI.Xaml; 3 | using Microsoft.UI.Xaml.Controls; 4 | using Microsoft.UI.Xaml.Controls.Primitives; 5 | using Microsoft.UI.Xaml.Data; 6 | using Microsoft.UI.Xaml.Input; 7 | using Microsoft.UI.Xaml.Media; 8 | using Microsoft.UI.Xaml.Navigation; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Diagnostics; 12 | using System.IO; 13 | using System.Linq; 14 | using System.Runtime.InteropServices.WindowsRuntime; 15 | using Windows.Foundation; 16 | using Windows.Foundation.Collections; 17 | 18 | // To learn more about WinUI, the WinUI project structure, 19 | // and more about our project templates, see: http://aka.ms/winui-project-info. 20 | 21 | namespace Demo.WinUI 22 | { 23 | /// 24 | /// An empty window that can be used on its own or navigated to within a Frame. 25 | /// 26 | public sealed partial class MainWindow : Window 27 | { 28 | DispatcherTimer timer; 29 | public MainWindow() 30 | { 31 | this.InitializeComponent(); 32 | timer = new DispatcherTimer(); 33 | timer.Interval = TimeSpan.FromSeconds(1); 34 | timer.Tick += Timer_Tick; 35 | danmaku.DanmakuStyle = NSDanmaku.Model.DanmakuBorderStyle.Stroke; 36 | } 37 | 38 | private async void btn_AddRoll_Click(object sender, RoutedEventArgs e) 39 | { 40 | await danmaku.AddScrollDanmu(new NSDanmaku.Model.DanmakuModel() 41 | { 42 | color = Colors.White, 43 | location = NSDanmaku.Model.DanmakuLocation.Scroll, 44 | size = 25, 45 | text = text.Text 46 | }, ck_own.IsChecked.Value); 47 | } 48 | 49 | private async void btn_AddTop_Click(object sender, RoutedEventArgs e) 50 | { 51 | await danmaku.AddTopDanmu(new NSDanmaku.Model.DanmakuModel() 52 | { 53 | color = Colors.Blue, 54 | location = NSDanmaku.Model.DanmakuLocation.Scroll, 55 | size = 25, 56 | text = text.Text 57 | }, ck_own.IsChecked.Value); 58 | } 59 | 60 | private async void btn_AddBottom_Click(object sender, RoutedEventArgs e) 61 | { 62 | await danmaku.AddBottomDanmu(new NSDanmaku.Model.DanmakuModel() 63 | { 64 | color = Colors.Red, 65 | location = NSDanmaku.Model.DanmakuLocation.Scroll, 66 | size = 25, 67 | text = text.Text 68 | }, ck_own.IsChecked.Value); 69 | } 70 | 71 | private void btn_Clear_Click(object sender, RoutedEventArgs e) 72 | { 73 | danmaku.ClearAll(); 74 | } 75 | private async void Timer_Tick(object sender, object e) 76 | { 77 | var danmu = danmakus.Where(x => Convert.ToInt32(x.time) == slider.Value); 78 | foreach (var item in danmu) 79 | { 80 | try 81 | { 82 | await danmaku.AddDanmu(item, false); 83 | } 84 | catch (Exception) 85 | { 86 | Debug.WriteLine("Can't add danmaku:" + item.source); 87 | } 88 | } 89 | slider.Value++; 90 | } 91 | List danmakus; 92 | private async void btn_Play_Click(object sender, RoutedEventArgs e) 93 | { 94 | if (danmakus == null) 95 | { 96 | try 97 | { 98 | NSDanmaku.WinUI.Helper.DanmakuParse danmakuParse = new NSDanmaku.WinUI.Helper.DanmakuParse(); 99 | danmakus = await danmakuParse.ParseBiliBili(11311248); 100 | } 101 | catch (Exception) 102 | { 103 | Debug.WriteLine("Can't load danmaku"); 104 | return; 105 | } 106 | 107 | } 108 | danmaku.ResumeDanmaku(); 109 | timer.Start(); 110 | } 111 | 112 | private void btn_Pause_Click(object sender, RoutedEventArgs e) 113 | { 114 | timer.Stop(); 115 | danmaku.PauseDanmaku(); 116 | 117 | } 118 | 119 | private void btn_GetAll_Click(object sender, RoutedEventArgs e) 120 | { 121 | var ls = danmaku.GetDanmakus(); 122 | Debug.WriteLine("Count:" + ls.Count); 123 | } 124 | 125 | private void ck_HideRoll_Checked(object sender, RoutedEventArgs e) 126 | { 127 | danmaku.HideDanmaku(NSDanmaku.Model.DanmakuLocation.Scroll); 128 | } 129 | 130 | private void ck_HideRoll_Unchecked(object sender, RoutedEventArgs e) 131 | { 132 | danmaku.ShowDanmaku(NSDanmaku.Model.DanmakuLocation.Scroll); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /Demo/MainPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.InteropServices.WindowsRuntime; 7 | using System.Threading.Tasks; 8 | using Windows.Foundation; 9 | using Windows.Foundation.Collections; 10 | using Windows.UI; 11 | using Windows.UI.Xaml; 12 | using Windows.UI.Xaml.Controls; 13 | using Windows.UI.Xaml.Controls.Primitives; 14 | using Windows.UI.Xaml.Data; 15 | using Windows.UI.Xaml.Input; 16 | using Windows.UI.Xaml.Media; 17 | using Windows.UI.Xaml.Navigation; 18 | 19 | // https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x804 上介绍了“空白页”项模板 20 | 21 | namespace Demo 22 | { 23 | /// 24 | /// 可用于自身或导航至 Frame 内部的空白页。 25 | /// 26 | public sealed partial class MainPage : Page 27 | { 28 | DispatcherTimer timer; 29 | public MainPage() 30 | { 31 | this.InitializeComponent(); 32 | timer = new DispatcherTimer(); 33 | timer.Interval = TimeSpan.FromSeconds(1); 34 | timer.Tick += Timer_Tick; 35 | } 36 | 37 | protected override void OnNavigatedTo(NavigationEventArgs e) 38 | { 39 | 40 | base.OnNavigatedTo(e); 41 | danmaku.DanmakuStyle = NSDanmaku.Model.DanmakuBorderStyle.Stroke; 42 | } 43 | 44 | private async void btn_AddRoll_Click(object sender, RoutedEventArgs e) 45 | { 46 | await danmaku.AddScrollDanmu(new NSDanmaku.Model.DanmakuModel() 47 | { 48 | color = Colors.White, 49 | location = NSDanmaku.Model.DanmakuLocation.Scroll, 50 | size = 25, 51 | text = text.Text 52 | }, ck_own.IsChecked.Value); 53 | } 54 | 55 | private async void btn_AddTop_Click(object sender, RoutedEventArgs e) 56 | { 57 | await danmaku.AddTopDanmu(new NSDanmaku.Model.DanmakuModel() 58 | { 59 | color = Colors.Blue, 60 | location = NSDanmaku.Model.DanmakuLocation.Scroll, 61 | size = 25, 62 | text = text.Text 63 | }, ck_own.IsChecked.Value); 64 | } 65 | 66 | private async void btn_AddBottom_Click(object sender, RoutedEventArgs e) 67 | { 68 | await danmaku.AddBottomDanmu(new NSDanmaku.Model.DanmakuModel() 69 | { 70 | color = Colors.Red, 71 | location = NSDanmaku.Model.DanmakuLocation.Scroll, 72 | size = 25, 73 | text = text.Text 74 | }, ck_own.IsChecked.Value); 75 | } 76 | 77 | private void btn_Clear_Click(object sender, RoutedEventArgs e) 78 | { 79 | danmaku.ClearAll(); 80 | } 81 | private async void Timer_Tick(object sender, object e) 82 | { 83 | var danmu = danmakus.Where(x => Convert.ToInt32(x.time) == slider.Value); 84 | foreach (var item in danmu) 85 | { 86 | try 87 | { 88 | await danmaku.AddDanmu(item, false); 89 | } 90 | catch (Exception) 91 | { 92 | Debug.WriteLine("Can't add danmaku:" + item.source); 93 | } 94 | } 95 | slider.Value++; 96 | } 97 | List danmakus; 98 | private async void btn_Play_Click(object sender, RoutedEventArgs e) 99 | { 100 | if (danmakus == null) 101 | { 102 | try 103 | { 104 | NSDanmaku.Helper.DanmakuParse danmakuParse = new NSDanmaku.Helper.DanmakuParse(); 105 | danmakus = await danmakuParse.ParseBiliBili(11311248); 106 | } 107 | catch (Exception) 108 | { 109 | Debug.WriteLine("Can't load danmaku"); 110 | return; 111 | } 112 | 113 | } 114 | danmaku.ResumeDanmaku(); 115 | timer.Start(); 116 | } 117 | 118 | private void btn_Pause_Click(object sender, RoutedEventArgs e) 119 | { 120 | timer.Stop(); 121 | danmaku.PauseDanmaku(); 122 | 123 | } 124 | 125 | private void btn_GetAll_Click(object sender, RoutedEventArgs e) 126 | { 127 | var ls = danmaku.GetDanmakus(); 128 | Debug.WriteLine("Count:" + ls.Count); 129 | } 130 | 131 | private void ck_HideRoll_Checked(object sender, RoutedEventArgs e) 132 | { 133 | danmaku.HideDanmaku(NSDanmaku.Model.DanmakuLocation.Scroll); 134 | } 135 | 136 | private void ck_HideRoll_Unchecked(object sender, RoutedEventArgs e) 137 | { 138 | danmaku.ShowDanmaku(NSDanmaku.Model.DanmakuLocation.Scroll); 139 | } 140 | } 141 | 142 | 143 | } 144 | -------------------------------------------------------------------------------- /Demo.WinUI/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 25 | 26 | 自己发送的 27 | 28 | 34 | 40 | 46 | 52 | 58 | 59 | 60 | 模拟播放 61 | 62 | 67 | 72 | 77 | 78 | / 79 | 80 | 81 | 85 | 89 | 隐藏滚动弹幕 90 | 91 | 92 | 显示区域 93 | 100 | 101 | 大小缩放 102 | 109 | 110 | 透明度 111 | 118 | 119 | 滚动速度 120 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /NSDanmaku/Controls/DanmakuItemControl.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Graphics.Canvas; 2 | using Microsoft.Graphics.Canvas.Geometry; 3 | using Microsoft.Graphics.Canvas.Text; 4 | using NSDanmaku.Model; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Runtime.ConstrainedExecution; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using Windows.Foundation; 12 | using Windows.Storage.Streams; 13 | using Windows.UI; 14 | using Windows.UI.Text; 15 | using Windows.UI.Xaml; 16 | using Windows.UI.Xaml.Controls; 17 | using Windows.UI.Xaml.Media; 18 | using Windows.UI.Xaml.Media.Imaging; 19 | 20 | namespace NSDanmaku.Controls 21 | { 22 | public class DanmakuItemControl 23 | { 24 | /// 25 | /// 创建重叠弹幕 26 | /// 27 | /// 28 | /// 29 | /// 30 | /// 31 | /// 32 | public static Grid CreateControlOverlap(float sizeZoom, bool bold, string fontFamily, DanmakuModel model) 33 | { 34 | //创建基础控件 35 | TextBlock tx = new TextBlock(); 36 | TextBlock tx2 = new TextBlock(); 37 | Grid grid = new Grid(); 38 | 39 | 40 | tx2.Text = model.text; 41 | tx.Text = model.text; 42 | if (bold) 43 | { 44 | tx.FontWeight = FontWeights.Bold; 45 | tx2.FontWeight = FontWeights.Bold; 46 | } 47 | if (fontFamily != "") 48 | { 49 | tx.FontFamily = new FontFamily(fontFamily); 50 | tx2.FontFamily = new FontFamily(fontFamily); 51 | } 52 | tx2.Foreground = new SolidColorBrush(GetBorderColor(model.color)); 53 | tx.Foreground = new SolidColorBrush(model.color); 54 | //弹幕大小 55 | double size = model.size * sizeZoom; 56 | 57 | tx2.FontSize = size; 58 | tx.FontSize = size; 59 | 60 | tx2.Margin = new Thickness(1); 61 | //grid包含弹幕文本信息 62 | 63 | grid.Children.Add(tx2); 64 | grid.Children.Add(tx); 65 | grid.Tag = model; 66 | return grid; 67 | } 68 | /// 69 | /// 创建无边框弹幕 70 | /// 71 | /// 72 | /// 73 | /// 74 | /// 75 | /// 76 | public static Grid CreateControlNoBorder(float sizeZoom, bool bold, string fontFamily, DanmakuModel model) 77 | { 78 | //创建基础控件 79 | TextBlock tx = new TextBlock(); 80 | 81 | Grid grid = new Grid(); 82 | 83 | tx.Text = model.text; 84 | if (bold) 85 | { 86 | tx.FontWeight = FontWeights.Bold; 87 | } 88 | if (fontFamily != "") 89 | { 90 | tx.FontFamily = new FontFamily(fontFamily); 91 | } 92 | tx.Foreground = new SolidColorBrush(model.color); 93 | //弹幕大小 94 | double size = model.size * sizeZoom; 95 | 96 | tx.FontSize = size; 97 | 98 | grid.Children.Add(tx); 99 | grid.Tag = model; 100 | return grid; 101 | } 102 | /// 103 | /// 创建图片弹幕 104 | /// 105 | /// 106 | /// 107 | /// 108 | /// 109 | /// 110 | public static Grid CreateImageControl(BitmapImage image) 111 | { 112 | //创建基础控件 113 | Image img = new Image(); 114 | img.Source = image; 115 | Grid grid = new Grid(); 116 | DanmakuModel model = new DanmakuModel() { text = "666666" }; 117 | grid.Tag = model; 118 | grid.Children.Add(img); 119 | return grid; 120 | } 121 | /// 122 | /// 创建边框弹幕 123 | /// 124 | /// 125 | /// 126 | /// 127 | /// 128 | /// 129 | public static async Task CreateControlBorder(float sizeZoom,bool bold, string fontFamily, DanmakuModel model) 130 | { 131 | if (Danmaku.LogicalDpi <= 0) 132 | { 133 | Danmaku.InitDanmakuDpi(); 134 | } 135 | float size = (float)model.size * (float)sizeZoom; 136 | 137 | CanvasDevice device = CanvasDevice.GetSharedDevice(); 138 | 139 | CanvasTextFormat fmt = new CanvasTextFormat() { FontSize = size }; 140 | var tb = new TextBlock { Text = model.text, FontSize = size, }; 141 | 142 | if (bold) 143 | { 144 | fmt.FontWeight = FontWeights.Bold; 145 | tb.FontWeight = FontWeights.Bold; 146 | } 147 | if (fontFamily != "") 148 | { 149 | fmt.FontFamily = fontFamily; 150 | tb.FontFamily = new FontFamily(fontFamily); 151 | } 152 | tb.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); 153 | 154 | var myBitmap = new CanvasRenderTarget(device, (float)tb.DesiredSize.Width, (float)tb.DesiredSize.Height, Danmaku.LogicalDpi); 155 | 156 | CanvasTextLayout canvasTextLayout = new CanvasTextLayout(device, model.text, fmt, (float)tb.DesiredSize.Width, (float)tb.DesiredSize.Height); 157 | 158 | CanvasGeometry combinedGeometry = CanvasGeometry.CreateText(canvasTextLayout); 159 | 160 | using (var ds = myBitmap.CreateDrawingSession()) 161 | { 162 | ds.Clear(Colors.Transparent); 163 | ds.DrawGeometry(combinedGeometry, GetBorderColor(model.color), 2f,new CanvasStrokeStyle() { 164 | DashStyle = CanvasDashStyle.Solid 165 | }); 166 | ds.FillGeometry(combinedGeometry, model.color); 167 | } 168 | Image image = new Image(); 169 | BitmapImage im = new BitmapImage(); 170 | using (InMemoryRandomAccessStream oStream = new InMemoryRandomAccessStream()) 171 | { 172 | await myBitmap.SaveAsync(oStream, CanvasBitmapFileFormat.Png, 1.0f); 173 | await im.SetSourceAsync(oStream); 174 | } 175 | image.Source = im; 176 | image.Stretch = Stretch.Uniform; 177 | Grid grid = new Grid(); 178 | 179 | grid.Tag = model; 180 | grid.Children.Add(image); 181 | 182 | return grid; 183 | 184 | } 185 | 186 | public static Color GetBorderColor(Color textColor) 187 | { 188 | var brightness = ((textColor.R * 299) + (textColor.G * 587) + (textColor.B * 114)) / 1000; 189 | return brightness > 70? Colors.Black: Colors.White; 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /NSDanmaku.WinUI/Controls/DanmakuItemControl.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Graphics.Canvas; 2 | using Microsoft.Graphics.Canvas.Geometry; 3 | using Microsoft.Graphics.Canvas.Text; 4 | using Microsoft.UI; 5 | using Microsoft.UI.Text; 6 | using Microsoft.UI.Xaml; 7 | using Microsoft.UI.Xaml.Controls; 8 | using Microsoft.UI.Xaml.Media; 9 | using Microsoft.UI.Xaml.Media.Imaging; 10 | using NSDanmaku.Model; 11 | using NSDanmaku.WinUI.Controls; 12 | using System; 13 | using System.Collections.Generic; 14 | using System.Linq; 15 | using System.Runtime.ConstrainedExecution; 16 | using System.Text; 17 | using System.Threading.Tasks; 18 | using Windows.Foundation; 19 | using Windows.Storage.Streams; 20 | using Windows.UI; 21 | 22 | 23 | namespace NSDanmaku.WinUI.Controls 24 | { 25 | public class DanmakuItemControl 26 | { 27 | /// 28 | /// 创建重叠弹幕 29 | /// 30 | /// 31 | /// 32 | /// 33 | /// 34 | /// 35 | public static Grid CreateControlOverlap(float sizeZoom, bool bold, string fontFamily, DanmakuModel model) 36 | { 37 | //创建基础控件 38 | TextBlock tx = new TextBlock(); 39 | TextBlock tx2 = new TextBlock(); 40 | Grid grid = new Grid(); 41 | 42 | 43 | tx2.Text = model.text; 44 | tx.Text = model.text; 45 | if (bold) 46 | { 47 | tx.FontWeight = FontWeights.Bold; 48 | tx2.FontWeight = FontWeights.Bold; 49 | } 50 | if (fontFamily != "") 51 | { 52 | tx.FontFamily = new FontFamily(fontFamily); 53 | tx2.FontFamily = new FontFamily(fontFamily); 54 | } 55 | tx2.Foreground = new SolidColorBrush(GetBorderColor(model.color)); 56 | tx.Foreground = new SolidColorBrush(model.color); 57 | //弹幕大小 58 | double size = model.size * sizeZoom; 59 | 60 | tx2.FontSize = size; 61 | tx.FontSize = size; 62 | 63 | tx2.Margin = new Thickness(1); 64 | //grid包含弹幕文本信息 65 | 66 | grid.Children.Add(tx2); 67 | grid.Children.Add(tx); 68 | grid.Tag = model; 69 | return grid; 70 | } 71 | /// 72 | /// 创建无边框弹幕 73 | /// 74 | /// 75 | /// 76 | /// 77 | /// 78 | /// 79 | public static Grid CreateControlNoBorder(float sizeZoom, bool bold, string fontFamily, DanmakuModel model) 80 | { 81 | //创建基础控件 82 | TextBlock tx = new TextBlock(); 83 | 84 | Grid grid = new Grid(); 85 | 86 | tx.Text = model.text; 87 | if (bold) 88 | { 89 | tx.FontWeight = FontWeights.Bold; 90 | } 91 | if (fontFamily != "") 92 | { 93 | tx.FontFamily = new FontFamily(fontFamily); 94 | } 95 | tx.Foreground = new SolidColorBrush(model.color); 96 | //弹幕大小 97 | double size = model.size * sizeZoom; 98 | 99 | tx.FontSize = size; 100 | 101 | grid.Children.Add(tx); 102 | grid.Tag = model; 103 | return grid; 104 | } 105 | /// 106 | /// 创建图片弹幕 107 | /// 108 | /// 109 | /// 110 | /// 111 | /// 112 | /// 113 | public static Grid CreateImageControl(BitmapImage image) 114 | { 115 | //创建基础控件 116 | Image img = new Image(); 117 | img.Source = image; 118 | Grid grid = new Grid(); 119 | DanmakuModel model = new DanmakuModel() { text = "666666" }; 120 | grid.Tag = model; 121 | grid.Children.Add(img); 122 | return grid; 123 | } 124 | /// 125 | /// 创建边框弹幕 126 | /// 127 | /// 128 | /// 129 | /// 130 | /// 131 | /// 132 | public static async Task CreateControlBorder(float sizeZoom,bool bold, string fontFamily, DanmakuModel model) 133 | { 134 | if (Danmaku.LogicalDpi <= 0) 135 | { 136 | Danmaku.InitDanmakuDpi(); 137 | } 138 | float size = (float)model.size * (float)sizeZoom; 139 | 140 | CanvasDevice device = CanvasDevice.GetSharedDevice(); 141 | 142 | CanvasTextFormat fmt = new CanvasTextFormat() { FontSize = size }; 143 | var tb = new TextBlock { Text = model.text, FontSize = size, }; 144 | 145 | if (bold) 146 | { 147 | fmt.FontWeight = FontWeights.Bold; 148 | tb.FontWeight = FontWeights.Bold; 149 | } 150 | if (fontFamily != "") 151 | { 152 | fmt.FontFamily = fontFamily; 153 | tb.FontFamily = new FontFamily(fontFamily); 154 | } 155 | tb.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); 156 | 157 | var myBitmap = new CanvasRenderTarget(device, (float)tb.DesiredSize.Width, (float)tb.DesiredSize.Height, Danmaku.LogicalDpi); 158 | 159 | CanvasTextLayout canvasTextLayout = new CanvasTextLayout(device, model.text, fmt, (float)tb.DesiredSize.Width, (float)tb.DesiredSize.Height); 160 | 161 | CanvasGeometry combinedGeometry = CanvasGeometry.CreateText(canvasTextLayout); 162 | 163 | using (var ds = myBitmap.CreateDrawingSession()) 164 | { 165 | ds.Clear(Colors.Transparent); 166 | ds.DrawGeometry(combinedGeometry, GetBorderColor(model.color), 2f,new CanvasStrokeStyle() { 167 | DashStyle = CanvasDashStyle.Solid 168 | }); 169 | ds.FillGeometry(combinedGeometry, model.color); 170 | } 171 | Image image = new Image(); 172 | BitmapImage im = new BitmapImage(); 173 | using (InMemoryRandomAccessStream oStream = new InMemoryRandomAccessStream()) 174 | { 175 | await myBitmap.SaveAsync(oStream, CanvasBitmapFileFormat.Png, 1.0f); 176 | await im.SetSourceAsync(oStream); 177 | } 178 | image.Source = im; 179 | image.Stretch = Stretch.Uniform; 180 | Grid grid = new Grid(); 181 | 182 | grid.Tag = model; 183 | grid.Children.Add(image); 184 | 185 | return grid; 186 | 187 | } 188 | 189 | public static Color GetBorderColor(Color textColor) 190 | { 191 | var brightness = ((textColor.R * 299) + (textColor.G * 587) + (textColor.B * 114)) / 1000; 192 | return brightness > 70? Colors.Black: Colors.White; 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /Demo/Demo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x86 7 | {30C4C949-95E5-4B7E-AD6E-099207050273} 8 | AppContainerExe 9 | Properties 10 | Demo 11 | Demo 12 | zh-CN 13 | UAP 14 | 10.0.19041.0 15 | 10.0.16299.0 16 | 14 17 | 512 18 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 19 | true 20 | Demo_TemporaryKey.pfx 21 | True 22 | Always 23 | x86|x64 24 | 25 | 26 | true 27 | bin\x86\Debug\ 28 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 29 | ;2008 30 | full 31 | x86 32 | false 33 | prompt 34 | true 35 | 36 | 37 | bin\x86\Release\ 38 | TRACE;NETFX_CORE;WINDOWS_UWP 39 | true 40 | ;2008 41 | pdbonly 42 | x86 43 | false 44 | prompt 45 | true 46 | true 47 | 48 | 49 | true 50 | bin\ARM\Debug\ 51 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 52 | ;2008 53 | full 54 | ARM 55 | false 56 | prompt 57 | true 58 | 59 | 60 | bin\ARM\Release\ 61 | TRACE;NETFX_CORE;WINDOWS_UWP 62 | true 63 | ;2008 64 | pdbonly 65 | ARM 66 | false 67 | prompt 68 | true 69 | true 70 | 71 | 72 | true 73 | bin\x64\Debug\ 74 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 75 | ;2008 76 | full 77 | x64 78 | false 79 | prompt 80 | true 81 | 82 | 83 | bin\x64\Release\ 84 | TRACE;NETFX_CORE;WINDOWS_UWP 85 | true 86 | ;2008 87 | pdbonly 88 | x64 89 | false 90 | prompt 91 | true 92 | true 93 | 94 | 95 | PackageReference 96 | 97 | 98 | 99 | App.xaml 100 | 101 | 102 | MainPage.xaml 103 | 104 | 105 | 106 | 107 | 108 | Designer 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | MSBuild:Compile 125 | Designer 126 | 127 | 128 | MSBuild:Compile 129 | Designer 130 | 131 | 132 | 133 | 134 | 6.2.9 135 | 136 | 137 | 138 | 139 | {964d5bcc-c063-45cc-aeea-89169e4a2e5e} 140 | NSDanmaku 141 | 142 | 143 | 144 | 14.0 145 | 146 | 147 | true 148 | bin\ARM64\Debug\ 149 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS 150 | ;2008 151 | true 152 | full 153 | ARM64 154 | false 155 | 7.3 156 | prompt 157 | true 158 | 159 | 160 | bin\ARM64\Release\ 161 | TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS 162 | true 163 | ;2008 164 | true 165 | pdbonly 166 | ARM64 167 | false 168 | 7.3 169 | prompt 170 | true 171 | 172 | 173 | 180 | -------------------------------------------------------------------------------- /NSDanmaku/NSDanmaku.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E} 8 | Library 9 | Properties 10 | NSDanmaku 11 | NSDanmaku 12 | zh-CN 13 | UAP 14 | 10.0.19041.0 15 | 10.0.16299.0 16 | 14 17 | 512 18 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 19 | 20 | 21 | AnyCPU 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 27 | prompt 28 | 4 29 | 30 | 31 | AnyCPU 32 | pdbonly 33 | true 34 | bin\Release\ 35 | TRACE;NETFX_CORE;WINDOWS_UWP 36 | prompt 37 | 4 38 | 39 | 40 | true 41 | 42 | 43 | x86 44 | true 45 | bin\x86\Debug\ 46 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 47 | ;2008 48 | full 49 | x86 50 | false 51 | prompt 52 | 53 | 54 | x86 55 | bin\x86\Release\ 56 | TRACE;NETFX_CORE;WINDOWS_UWP 57 | true 58 | ;2008 59 | pdbonly 60 | x86 61 | false 62 | prompt 63 | 64 | 65 | ARM 66 | true 67 | bin\ARM\Debug\ 68 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 69 | ;2008 70 | full 71 | ARM 72 | false 73 | prompt 74 | 75 | 76 | ARM 77 | bin\ARM\Release\ 78 | TRACE;NETFX_CORE;WINDOWS_UWP 79 | true 80 | ;2008 81 | pdbonly 82 | ARM 83 | false 84 | prompt 85 | 86 | 87 | x64 88 | true 89 | bin\x64\Debug\ 90 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 91 | ;2008 92 | full 93 | x64 94 | false 95 | prompt 96 | 97 | 98 | x64 99 | bin\x64\Release\ 100 | TRACE;NETFX_CORE;WINDOWS_UWP 101 | true 102 | ;2008 103 | pdbonly 104 | x64 105 | false 106 | prompt 107 | 108 | 109 | PackageReference 110 | 111 | 112 | 113 | Danmaku.xaml 114 | 115 | 116 | 117 | 118 | TantanDialog.xaml 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 6.2.9 132 | 133 | 134 | 13.0.1 135 | 136 | 137 | 1.26.0 138 | 139 | 140 | 141 | 142 | MSBuild:Compile 143 | Designer 144 | 145 | 146 | Designer 147 | MSBuild:Compile 148 | 149 | 150 | 151 | 152 | 153 | 154 | 14.0 155 | 156 | 157 | 158 | 159 | 160 | true 161 | bin\ARM64\Debug\ 162 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 163 | ;2008 164 | true 165 | full 166 | ARM64 167 | false 168 | 7.3 169 | prompt 170 | 171 | 172 | bin\ARM64\Release\ 173 | TRACE;NETFX_CORE;WINDOWS_UWP 174 | true 175 | ;2008 176 | true 177 | pdbonly 178 | ARM64 179 | false 180 | 7.3 181 | prompt 182 | 183 | 184 | 191 | -------------------------------------------------------------------------------- /NSDanmaku.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.14.36310.24 d17.14 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NSDanmaku", "NSDanmaku\NSDanmaku.csproj", "{964D5BCC-C063-45CC-AEEA-89169E4A2E5E}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo", "Demo\Demo.csproj", "{30C4C949-95E5-4B7E-AD6E-099207050273}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NSDanmaku.WinUI", "NSDanmaku.WinUI\NSDanmaku.WinUI.csproj", "{58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo.WinUI", "Demo.WinUI\Demo.WinUI.csproj", "{6D4DF270-2370-4DD3-93C8-95ADA1623864}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "解决方案项", "解决方案项", "{9FA3D6BD-1EC1-3BA5-80CB-CE02773A58D5}" 15 | ProjectSection(SolutionItems) = preProject 16 | README.md = README.md 17 | EndProjectSection 18 | EndProject 19 | Global 20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 21 | Debug|Any CPU = Debug|Any CPU 22 | Debug|ARM = Debug|ARM 23 | Debug|ARM64 = Debug|ARM64 24 | Debug|x64 = Debug|x64 25 | Debug|x86 = Debug|x86 26 | Release|Any CPU = Release|Any CPU 27 | Release|ARM = Release|ARM 28 | Release|ARM64 = Release|ARM64 29 | Release|x64 = Release|x64 30 | Release|x86 = Release|x86 31 | EndGlobalSection 32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 33 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Debug|ARM.ActiveCfg = Debug|ARM 36 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Debug|ARM.Build.0 = Debug|ARM 37 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Debug|ARM64.ActiveCfg = Debug|ARM64 38 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Debug|ARM64.Build.0 = Debug|ARM64 39 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Debug|x64.ActiveCfg = Debug|x64 40 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Debug|x64.Build.0 = Debug|x64 41 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Debug|x86.ActiveCfg = Debug|x86 42 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Debug|x86.Build.0 = Debug|x86 43 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Release|ARM.ActiveCfg = Release|ARM 46 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Release|ARM.Build.0 = Release|ARM 47 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Release|ARM64.ActiveCfg = Release|ARM64 48 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Release|ARM64.Build.0 = Release|ARM64 49 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Release|x64.ActiveCfg = Release|x64 50 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Release|x64.Build.0 = Release|x64 51 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Release|x86.ActiveCfg = Release|x86 52 | {964D5BCC-C063-45CC-AEEA-89169E4A2E5E}.Release|x86.Build.0 = Release|x86 53 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Debug|Any CPU.ActiveCfg = Debug|x86 54 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Debug|ARM.ActiveCfg = Debug|ARM 55 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Debug|ARM.Build.0 = Debug|ARM 56 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Debug|ARM.Deploy.0 = Debug|ARM 57 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Debug|ARM64.ActiveCfg = Debug|ARM64 58 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Debug|ARM64.Build.0 = Debug|ARM64 59 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Debug|ARM64.Deploy.0 = Debug|ARM64 60 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Debug|x64.ActiveCfg = Debug|x64 61 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Debug|x64.Build.0 = Debug|x64 62 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Debug|x64.Deploy.0 = Debug|x64 63 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Debug|x86.ActiveCfg = Debug|x86 64 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Debug|x86.Build.0 = Debug|x86 65 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Debug|x86.Deploy.0 = Debug|x86 66 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Release|Any CPU.ActiveCfg = Release|x86 67 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Release|ARM.ActiveCfg = Release|ARM 68 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Release|ARM.Build.0 = Release|ARM 69 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Release|ARM.Deploy.0 = Release|ARM 70 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Release|ARM64.ActiveCfg = Release|ARM64 71 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Release|ARM64.Build.0 = Release|ARM64 72 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Release|ARM64.Deploy.0 = Release|ARM64 73 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Release|x64.ActiveCfg = Release|x64 74 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Release|x64.Build.0 = Release|x64 75 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Release|x64.Deploy.0 = Release|x64 76 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Release|x86.ActiveCfg = Release|x86 77 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Release|x86.Build.0 = Release|x86 78 | {30C4C949-95E5-4B7E-AD6E-099207050273}.Release|x86.Deploy.0 = Release|x86 79 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 80 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Debug|Any CPU.Build.0 = Debug|Any CPU 81 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Debug|ARM.ActiveCfg = Debug|Any CPU 82 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Debug|ARM.Build.0 = Debug|Any CPU 83 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Debug|ARM64.ActiveCfg = Debug|Any CPU 84 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Debug|ARM64.Build.0 = Debug|Any CPU 85 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Debug|x64.ActiveCfg = Debug|Any CPU 86 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Debug|x64.Build.0 = Debug|Any CPU 87 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Debug|x86.ActiveCfg = Debug|Any CPU 88 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Debug|x86.Build.0 = Debug|Any CPU 89 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Release|Any CPU.ActiveCfg = Release|Any CPU 90 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Release|Any CPU.Build.0 = Release|Any CPU 91 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Release|ARM.ActiveCfg = Release|Any CPU 92 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Release|ARM.Build.0 = Release|Any CPU 93 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Release|ARM64.ActiveCfg = Release|Any CPU 94 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Release|ARM64.Build.0 = Release|Any CPU 95 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Release|x64.ActiveCfg = Release|Any CPU 96 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Release|x64.Build.0 = Release|Any CPU 97 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Release|x86.ActiveCfg = Release|Any CPU 98 | {58B2A2A2-4225-4C6E-A286-2CED8DCBB2DB}.Release|x86.Build.0 = Release|Any CPU 99 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Debug|Any CPU.ActiveCfg = Debug|x64 100 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Debug|Any CPU.Build.0 = Debug|x64 101 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Debug|Any CPU.Deploy.0 = Debug|x64 102 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Debug|ARM.ActiveCfg = Debug|x64 103 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Debug|ARM.Build.0 = Debug|x64 104 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Debug|ARM.Deploy.0 = Debug|x64 105 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Debug|ARM64.ActiveCfg = Debug|ARM64 106 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Debug|ARM64.Build.0 = Debug|ARM64 107 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Debug|ARM64.Deploy.0 = Debug|ARM64 108 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Debug|x64.ActiveCfg = Debug|x64 109 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Debug|x64.Build.0 = Debug|x64 110 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Debug|x64.Deploy.0 = Debug|x64 111 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Debug|x86.ActiveCfg = Debug|x86 112 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Debug|x86.Build.0 = Debug|x86 113 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Debug|x86.Deploy.0 = Debug|x86 114 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Release|Any CPU.ActiveCfg = Release|x64 115 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Release|Any CPU.Build.0 = Release|x64 116 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Release|Any CPU.Deploy.0 = Release|x64 117 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Release|ARM.ActiveCfg = Release|x64 118 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Release|ARM.Build.0 = Release|x64 119 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Release|ARM.Deploy.0 = Release|x64 120 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Release|ARM64.ActiveCfg = Release|ARM64 121 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Release|ARM64.Build.0 = Release|ARM64 122 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Release|ARM64.Deploy.0 = Release|ARM64 123 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Release|x64.ActiveCfg = Release|x64 124 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Release|x64.Build.0 = Release|x64 125 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Release|x64.Deploy.0 = Release|x64 126 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Release|x86.ActiveCfg = Release|x86 127 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Release|x86.Build.0 = Release|x86 128 | {6D4DF270-2370-4DD3-93C8-95ADA1623864}.Release|x86.Deploy.0 = Release|x86 129 | EndGlobalSection 130 | GlobalSection(SolutionProperties) = preSolution 131 | HideSolutionNode = FALSE 132 | EndGlobalSection 133 | GlobalSection(ExtensibilityGlobals) = postSolution 134 | SolutionGuid = {FE90CAF7-9D11-4041-BF7D-F8AF69BFD893} 135 | EndGlobalSection 136 | EndGlobal 137 | -------------------------------------------------------------------------------- /NSDanmaku/Controls/Danmaku.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Graphics.Canvas; 2 | using Microsoft.Graphics.Canvas.Geometry; 3 | using Microsoft.Graphics.Canvas.Text; 4 | using NSDanmaku.Model; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Runtime.InteropServices.WindowsRuntime; 11 | using System.Threading.Tasks; 12 | using Windows.Foundation; 13 | using Windows.Foundation.Collections; 14 | using Windows.Storage.Streams; 15 | using Windows.UI; 16 | using Windows.UI.Text; 17 | using Windows.UI.Xaml; 18 | using Windows.UI.Xaml.Controls; 19 | using Windows.UI.Xaml.Controls.Primitives; 20 | using Windows.UI.Xaml.Data; 21 | using Windows.UI.Xaml.Input; 22 | using Windows.UI.Xaml.Media; 23 | using Windows.UI.Xaml.Media.Animation; 24 | using Windows.UI.Xaml.Media.Imaging; 25 | using Windows.UI.Xaml.Navigation; 26 | 27 | namespace NSDanmaku.Controls 28 | { 29 | public sealed partial class Danmaku : UserControl 30 | { 31 | public static float LogicalDpi { get; set; } = 0; 32 | /// 33 | /// 初始化弹幕DPI 34 | /// 35 | public static void InitDanmakuDpi() 36 | { 37 | try 38 | { 39 | Windows.Graphics.Display.DisplayInformation displayInformation = Windows.Graphics.Display.DisplayInformation.GetForCurrentView(); 40 | LogicalDpi = displayInformation.LogicalDpi; 41 | } 42 | catch (Exception) 43 | { 44 | LogicalDpi = 96f; 45 | } 46 | 47 | } 48 | 49 | 50 | public Danmaku() 51 | { 52 | this.InitializeComponent(); 53 | DanmakuArea = 1.0; 54 | DanmakuBold = false; 55 | DanmakuFontFamily = ""; 56 | } 57 | #region 弹幕属性 58 | /// 59 | /// 字体大小缩放,电脑推荐默认1.0,手机推荐0.5 60 | /// 61 | public double DanmakuSizeZoom 62 | { 63 | get { return (double)GetValue(DanmakuSizeZoomProperty); } 64 | set { SetValue(DanmakuSizeZoomProperty, value); } 65 | } 66 | 67 | public static readonly DependencyProperty DanmakuSizeZoomProperty = 68 | DependencyProperty.Register("DanmakuSizeZoom", typeof(double), typeof(Danmaku), new PropertyMetadata(1.0, OnDanmakuSizeZoomChanged)); 69 | 70 | private static void OnDanmakuSizeZoomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 71 | { 72 | var value = Convert.ToDouble(e.NewValue); 73 | if (value > 3) 74 | { 75 | value = 3; 76 | } 77 | if (value < 0.1) 78 | { 79 | value = 0.1; 80 | } 81 | //DanmakuSizeZoom = value; 82 | 83 | ((Danmaku)d).SetFontDanmakuSizeZoom(value); 84 | } 85 | private void SetFontDanmakuSizeZoom(double value) 86 | { 87 | 88 | SetRows(this.ActualHeight); 89 | foreach (var item in grid_Scroll.Children) 90 | { 91 | var grid = item as Grid; 92 | var m = grid.Tag as DanmakuModel; 93 | foreach (var tb in grid.Children) 94 | { 95 | if (tb is TextBlock) 96 | { 97 | (tb as TextBlock).FontSize = Convert.ToInt32(m.size) * DanmakuSizeZoom; 98 | } 99 | } 100 | 101 | } 102 | foreach (var item in grid_Top.Children) 103 | { 104 | var grid = item as Grid; 105 | var m = grid.Tag as DanmakuModel; 106 | foreach (var tb in grid.Children) 107 | { 108 | if (tb is TextBlock) 109 | { 110 | (tb as TextBlock).FontSize = Convert.ToInt32(m.size) * DanmakuSizeZoom; 111 | } 112 | } 113 | 114 | } 115 | foreach (var item in grid_Bottom.Children) 116 | { 117 | var grid = item as Grid; 118 | var m = grid.Tag as DanmakuModel; 119 | foreach (var tb in grid.Children) 120 | { 121 | if (tb is TextBlock) 122 | { 123 | (tb as TextBlock).FontSize = Convert.ToInt32(m.size) * DanmakuSizeZoom; 124 | } 125 | } 126 | 127 | } 128 | } 129 | 130 | /// 131 | /// 滚动弹幕动画持续时间,单位:秒,越小弹幕移动速度越快 132 | /// 133 | public int DanmakuDuration 134 | { 135 | get { return (int)GetValue(DanmakuDurationProperty); } 136 | set { SetValue(DanmakuDurationProperty, value); } 137 | } 138 | 139 | public static readonly DependencyProperty DanmakuDurationProperty = 140 | DependencyProperty.Register("DanmakuDuration", typeof(int), typeof(Danmaku), new PropertyMetadata(10, OnDanmakuDurationChanged)); 141 | private static void OnDanmakuDurationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 142 | { 143 | var value = Convert.ToInt32(e.NewValue); 144 | if (value <= 0) 145 | { 146 | value = 1; 147 | } 148 | ((Danmaku)d).SetDanmakuDuration(value); 149 | } 150 | public void SetDanmakuDuration(int value) 151 | { 152 | DanmakuDuration = value; 153 | } 154 | 155 | 156 | /// 157 | /// 弹幕是否加粗 158 | /// 159 | public bool DanmakuBold 160 | { 161 | get { return (bool)GetValue(DanmakuBoldProperty); } 162 | set { SetValue(DanmakuBoldProperty, value); } 163 | } 164 | 165 | public static readonly DependencyProperty DanmakuBoldProperty = 166 | DependencyProperty.Register("DanmakuBold", typeof(bool), typeof(Danmaku), new PropertyMetadata(0)); 167 | 168 | 169 | /// 170 | /// 弹幕字体名称 171 | /// 172 | public string DanmakuFontFamily 173 | { 174 | get { return (string)GetValue(DanmakuFontFamilyProperty); } 175 | set { SetValue(DanmakuFontFamilyProperty, value); } 176 | } 177 | 178 | 179 | public static readonly DependencyProperty DanmakuFontFamilyProperty = 180 | DependencyProperty.Register("DanmakuFontFamily", typeof(string), typeof(Danmaku), new PropertyMetadata(0)); 181 | 182 | 183 | /// 184 | /// 弹幕样式 185 | /// 186 | public DanmakuBorderStyle DanmakuStyle 187 | { 188 | get { return (DanmakuBorderStyle)GetValue(DanmakuStyleProperty); } 189 | set { SetValue(DanmakuStyleProperty, value); } 190 | } 191 | public static readonly DependencyProperty DanmakuStyleProperty = 192 | DependencyProperty.Register("DanmakuStyle", typeof(DanmakuBorderStyle), typeof(Danmaku), new PropertyMetadata(DanmakuBorderStyle.Stroke)); 193 | 194 | 195 | /// 196 | /// 弹幕显示区域,取值0.1-1.0 197 | /// 198 | public double DanmakuArea 199 | { 200 | get { return (double)GetValue(DanmakuAreaProperty); } 201 | set { SetValue(DanmakuAreaProperty, value); } 202 | } 203 | 204 | public static readonly DependencyProperty DanmakuAreaProperty = 205 | DependencyProperty.Register("DanmakuArea", typeof(double), typeof(Danmaku), new PropertyMetadata(1, OnDanmakuAreaChanged)); 206 | 207 | private static void OnDanmakuAreaChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 208 | { 209 | var value = Convert.ToDouble(e.NewValue); 210 | if (value <=0) 211 | { 212 | value = 0.1; 213 | } 214 | if (value > 1) 215 | { 216 | value = 1; 217 | } 218 | 219 | ((Danmaku)d).SetDanmakuArea(value); 220 | } 221 | public void SetDanmakuArea(double value) 222 | { 223 | DanmakuArea = value; 224 | } 225 | #endregion 226 | 227 | //动画管理 228 | List topBottomStoryList = new List(); 229 | List rollStoryList = new List(); 230 | List positionStoryList = new List(); 231 | 232 | protected override Size MeasureOverride(Size availableSize) 233 | { 234 | SetRows(availableSize.Height); 235 | return base.MeasureOverride(availableSize); 236 | } 237 | 238 | private void SetRows(double height) 239 | { 240 | 241 | var txt = new TextBlock() { 242 | Text="测试test", 243 | FontSize=25*DanmakuSizeZoom, 244 | }; 245 | txt.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); 246 | var rowHieght = txt.DesiredSize.Height; 247 | 248 | 249 | 250 | //将全部行去除 251 | grid_Top.RowDefinitions.Clear(); 252 | grid_Bottom.RowDefinitions.Clear(); 253 | grid_Scroll.RowDefinitions.Clear(); 254 | 255 | int num = Convert.ToInt32(height / rowHieght); 256 | // int pnum = Convert.ToInt32(height / rowHieght); 257 | 258 | //for (int i = 0; i < num; i++) 259 | //{ 260 | // grid_Bottom.RowDefinitions.Add(new RowDefinition()); 261 | // grid_Top.RowDefinitions.Add(new RowDefinition()); 262 | //} 263 | for (int i = 0; i < num; i++) 264 | { 265 | grid_Bottom.RowDefinitions.Add(new RowDefinition()); 266 | grid_Top.RowDefinitions.Add(new RowDefinition()); 267 | grid_Scroll.RowDefinitions.Add(new RowDefinition()); 268 | } 269 | } 270 | private int GetTopAvailableRow() 271 | { 272 | 273 | var max = grid_Top.RowDefinitions.Count/2; 274 | 275 | for (int i = 0; i < max; i++) 276 | { 277 | 278 | var row = grid_Top.Children.FirstOrDefault(x=>Grid.GetRow((x as Grid)) == i); 279 | if (row!=null) 280 | { 281 | continue; 282 | } 283 | else 284 | { 285 | return i; 286 | } 287 | 288 | } 289 | return -1; 290 | } 291 | private int GetBottomAvailableRow() 292 | { 293 | 294 | var max = grid_Bottom.RowDefinitions.Count/2; 295 | for (int i = 1; i <= max; i++) 296 | { 297 | var rowNum = grid_Bottom.RowDefinitions.Count - i; 298 | var row = grid_Bottom.Children.FirstOrDefault(x => Grid.GetRow((x as Grid)) == rowNum); 299 | if (row != null) 300 | { 301 | continue; 302 | } 303 | else 304 | { 305 | return rowNum; 306 | } 307 | } 308 | //for (int i = grid_Bottom.RowDefinitions.Count; i >= 0; i--) 309 | //{ 310 | // var row = grid_Bottom.Children.FirstOrDefault(x => Grid.GetRow((x as Grid)) == i); 311 | // if (row != null) 312 | // { 313 | // continue; 314 | // } 315 | // else 316 | // { 317 | // if (i>=max) 318 | // { 319 | // return i; 320 | // } 321 | 322 | // } 323 | 324 | //} 325 | return -1; 326 | } 327 | private int GetScrollAvailableRow(Grid item) 328 | { 329 | var width = grid_Scroll.ActualWidth; 330 | //计算弹幕尺寸 331 | item.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); 332 | var newWidth = item.DesiredSize.Width; 333 | if (newWidth <= 0) return -1; 334 | 335 | var max = grid_Scroll.RowDefinitions.Count*DanmakuArea; 336 | 337 | for (int i = 0; i < max; i++) 338 | { 339 | //1、检查当前行是否存在弹幕 340 | var lastItem=grid_Scroll.Children.LastOrDefault(x => Grid.GetRow((x as Grid)) == i); 341 | if (lastItem == null) 342 | { 343 | return i; 344 | } 345 | 346 | var lastWidth = (lastItem as Grid).ActualWidth; 347 | var lastX = (lastItem.RenderTransform as TranslateTransform).X; 348 | 349 | //2、前弹幕必须已经完全从右侧移动完毕 350 | if (lastX > width- lastWidth) 351 | { 352 | continue; 353 | } 354 | //3、后弹幕速度小于等于前弹幕速度 355 | var lastSpeed = (lastWidth + width) / DanmakuDuration; 356 | var newSpeed = (newWidth + width) / DanmakuDuration; 357 | if (newSpeed<= lastSpeed) 358 | { 359 | return i; 360 | } 361 | //4、弹幕移动期间不会重叠 362 | var runDistance =width- lastX; 363 | var t1 = (runDistance - newWidth) / (newSpeed - lastSpeed); 364 | var t2 = lastX / lastSpeed; 365 | if (t1 > t2) 366 | { 367 | return i; 368 | } 369 | } 370 | return -1; 371 | } 372 | 373 | 374 | private async Task CreateNewDanmuControl(DanmakuModel m) 375 | { 376 | switch (DanmakuStyle) 377 | { 378 | case DanmakuBorderStyle.WithoutStroke: 379 | return DanmakuItemControl.CreateControlNoBorder((float)DanmakuSizeZoom, DanmakuBold, DanmakuFontFamily, m); 380 | case DanmakuBorderStyle.Stroke: 381 | return await DanmakuItemControl.CreateControlBorder((float)DanmakuSizeZoom, DanmakuBold, DanmakuFontFamily, m); 382 | default: 383 | return DanmakuItemControl.CreateControlOverlap((float)DanmakuSizeZoom, DanmakuBold, DanmakuFontFamily, m); 384 | } 385 | 386 | } 387 | /// 388 | /// 添加直播滚动弹幕 389 | /// 390 | /// 参数 391 | /// 是否自己发送的 392 | /// 颜色 393 | public async void AddLiveDanmu(string text, bool own, Color? color) 394 | { 395 | if (color == null) 396 | { 397 | color = Colors.White; 398 | } 399 | var m = new DanmakuModel() 400 | { 401 | text = text, 402 | color = color.Value, 403 | location = DanmakuLocation.Scroll, 404 | size = 25 405 | }; 406 | Grid grid = await CreateNewDanmuControl(m); 407 | if (own) 408 | { 409 | grid.BorderBrush = new SolidColorBrush(color.Value); 410 | grid.BorderThickness = new Thickness(1); 411 | } 412 | var r = GetScrollAvailableRow(grid); 413 | if (r == -1) 414 | { 415 | return; 416 | } 417 | Grid.SetRow(grid, r); 418 | grid.HorizontalAlignment = HorizontalAlignment.Left; 419 | grid.VerticalAlignment = VerticalAlignment.Center; 420 | grid_Scroll.Children.Add(grid); 421 | grid_Scroll.UpdateLayout(); 422 | 423 | TranslateTransform moveTransform = new TranslateTransform(); 424 | moveTransform.X = gv.ActualWidth; 425 | grid.RenderTransform = moveTransform; 426 | 427 | //创建动画 428 | Duration duration = new Duration(TimeSpan.FromSeconds(DanmakuDuration)); 429 | DoubleAnimation myDoubleAnimationX = new DoubleAnimation(); 430 | myDoubleAnimationX.Duration = duration; 431 | //创建故事版 432 | Storyboard moveStoryboard = new Storyboard(); 433 | moveStoryboard.Duration = duration; 434 | myDoubleAnimationX.To = -(grid.ActualWidth);//到达 435 | moveStoryboard.Children.Add(myDoubleAnimationX); 436 | Storyboard.SetTarget(myDoubleAnimationX, moveTransform); 437 | //故事版加入动画 438 | Storyboard.SetTargetProperty(myDoubleAnimationX, "X"); 439 | rollStoryList.Add(moveStoryboard); 440 | 441 | moveStoryboard.Completed += new EventHandler((senders, obj) => 442 | { 443 | grid_Scroll.Children.Remove(grid); 444 | grid.Children.Clear(); 445 | grid = null; 446 | rollStoryList.Remove(moveStoryboard); 447 | moveStoryboard.Stop(); 448 | moveStoryboard = null; 449 | 450 | }); 451 | moveStoryboard.Begin(); 452 | } 453 | /// 454 | /// 添加一条弹幕 455 | /// 456 | /// 457 | /// 458 | /// 459 | public async Task AddDanmu(DanmakuModel m, bool own) 460 | { 461 | switch (m.location) 462 | { 463 | case DanmakuLocation.Scroll: 464 | await AddScrollDanmu(m, own); 465 | break; 466 | case DanmakuLocation.Top: 467 | await AddTopDanmu(m, own); 468 | break; 469 | case DanmakuLocation.Bottom: 470 | await AddBottomDanmu(m, own); 471 | break; 472 | case DanmakuLocation.Position: 473 | await AddPositionDanmu(m); 474 | break; 475 | default: 476 | //await AddScrollDanmu(m, own); 477 | break; 478 | } 479 | } 480 | 481 | 482 | /// 483 | /// 添加滚动弹幕 484 | /// 485 | /// 参数 486 | /// 是否自己发送的 487 | public async Task AddScrollDanmu(DanmakuModel m, bool own) 488 | { 489 | Grid grid = await CreateNewDanmuControl(m); 490 | 491 | if (own) 492 | { 493 | grid.BorderBrush = new SolidColorBrush(m.color); 494 | grid.BorderThickness = new Thickness(1); 495 | } 496 | var r = GetScrollAvailableRow(grid); 497 | if (r == -1) 498 | { 499 | grid = null; 500 | return; 501 | } 502 | 503 | Grid.SetRow(grid, r); 504 | grid.HorizontalAlignment = HorizontalAlignment.Left; 505 | grid.VerticalAlignment = VerticalAlignment.Center; 506 | grid_Scroll.Children.Add(grid); 507 | grid_Scroll.UpdateLayout(); 508 | 509 | TranslateTransform moveTransform = new TranslateTransform(); 510 | moveTransform.X = gv.ActualWidth; 511 | grid.RenderTransform = moveTransform; 512 | 513 | //创建动画 514 | Duration duration = new Duration(TimeSpan.FromSeconds(DanmakuDuration)); 515 | DoubleAnimation myDoubleAnimationX = new DoubleAnimation(); 516 | myDoubleAnimationX.Duration = duration; 517 | //创建故事版 518 | Storyboard moveStoryboard = new Storyboard(); 519 | moveStoryboard.Duration = duration; 520 | myDoubleAnimationX.To = -(grid.ActualWidth);//到达 521 | moveStoryboard.Children.Add(myDoubleAnimationX); 522 | Storyboard.SetTarget(myDoubleAnimationX, moveTransform); 523 | //故事版加入动画 524 | Storyboard.SetTargetProperty(myDoubleAnimationX, "X"); 525 | rollStoryList.Add(moveStoryboard); 526 | 527 | moveStoryboard.Completed += new EventHandler((senders, obj) => 528 | { 529 | grid_Scroll.Children.Remove(grid); 530 | grid.Children.Clear(); 531 | grid = null; 532 | rollStoryList.Remove(moveStoryboard); 533 | moveStoryboard.Stop(); 534 | moveStoryboard = null; 535 | }); 536 | moveStoryboard.Begin(); 537 | 538 | 539 | } 540 | 541 | /// 542 | /// 添加图片滚动弹幕 543 | /// 544 | /// 参数 545 | public void AddScrollImageDanmu(BitmapImage m) 546 | { 547 | Grid grid = null; 548 | grid = DanmakuItemControl.CreateImageControl(m); 549 | var r = GetScrollAvailableRow(grid); 550 | if (r == -1) 551 | { 552 | return; 553 | } 554 | Grid.SetRow(grid, r); 555 | grid.HorizontalAlignment = HorizontalAlignment.Left; 556 | grid.VerticalAlignment = VerticalAlignment.Center; 557 | grid_Scroll.Children.Add(grid); 558 | grid_Scroll.UpdateLayout(); 559 | 560 | TranslateTransform moveTransform = new TranslateTransform(); 561 | moveTransform.X = gv.ActualWidth; 562 | grid.RenderTransform = moveTransform; 563 | 564 | //创建动画 565 | Duration duration = new Duration(TimeSpan.FromSeconds(DanmakuDuration)); 566 | DoubleAnimation myDoubleAnimationX = new DoubleAnimation(); 567 | myDoubleAnimationX.Duration = duration; 568 | //创建故事版 569 | Storyboard moveStoryboard = new Storyboard(); 570 | moveStoryboard.Duration = duration; 571 | myDoubleAnimationX.To = -(grid.ActualWidth);//到达 572 | moveStoryboard.Children.Add(myDoubleAnimationX); 573 | Storyboard.SetTarget(myDoubleAnimationX, moveTransform); 574 | //故事版加入动画 575 | Storyboard.SetTargetProperty(myDoubleAnimationX, "X"); 576 | rollStoryList.Add(moveStoryboard); 577 | 578 | moveStoryboard.Completed += new EventHandler((senders, obj) => 579 | { 580 | 581 | grid_Scroll.Children.Remove(grid); 582 | grid = null; 583 | rollStoryList.Remove(moveStoryboard); 584 | 585 | }); 586 | moveStoryboard.Begin(); 587 | 588 | 589 | } 590 | /// 591 | /// 添加顶部弹幕 592 | /// 593 | /// 参数 594 | /// 是否自己发送的 595 | public async Task AddTopDanmu(DanmakuModel m, bool own) 596 | { 597 | 598 | Grid grid = await CreateNewDanmuControl(m); 599 | if (own) 600 | { 601 | grid.BorderBrush = new SolidColorBrush(m.color); 602 | grid.BorderThickness = new Thickness(1); 603 | } 604 | 605 | var r = GetTopAvailableRow(); 606 | if (r == -1) 607 | { 608 | return; 609 | } 610 | 611 | grid.HorizontalAlignment = HorizontalAlignment.Center; 612 | grid.VerticalAlignment = VerticalAlignment.Top; 613 | Grid.SetRow(grid, r); 614 | grid_Top.Children.Add(grid); 615 | 616 | 617 | //创建空转换动画 618 | TranslateTransform moveTransform = new TranslateTransform(); 619 | //创建动画 620 | Duration duration = new Duration(TimeSpan.FromSeconds(5)); 621 | DoubleAnimation myDoubleAnimationX = new DoubleAnimation(); 622 | myDoubleAnimationX.Duration = duration; 623 | //创建故事版 624 | Storyboard moveStoryboard = new Storyboard(); 625 | moveStoryboard.Duration = duration; 626 | moveStoryboard.Children.Add(myDoubleAnimationX); 627 | Storyboard.SetTarget(myDoubleAnimationX, moveTransform); 628 | //故事版加入动画 629 | Storyboard.SetTargetProperty(myDoubleAnimationX, "X"); 630 | topBottomStoryList.Add(moveStoryboard); 631 | 632 | moveStoryboard.Completed += new EventHandler((senders, obj) => 633 | { 634 | grid_Top.Children.Remove(grid); 635 | grid.Children.Clear(); 636 | grid = null; 637 | topBottomStoryList.Remove(moveStoryboard); 638 | moveStoryboard.Stop(); 639 | moveStoryboard = null; 640 | 641 | }); 642 | moveStoryboard.Begin(); 643 | } 644 | /// 645 | /// 添加底部弹幕 646 | /// 647 | /// 参数 648 | /// 是否自己发送的 649 | public async Task AddBottomDanmu(DanmakuModel m, bool own) 650 | { 651 | Grid grid = await CreateNewDanmuControl(m); 652 | if (own) 653 | { 654 | grid.BorderBrush = new SolidColorBrush(m.color); 655 | grid.BorderThickness = new Thickness(1); 656 | } 657 | grid.HorizontalAlignment = HorizontalAlignment.Center; 658 | grid.VerticalAlignment = VerticalAlignment.Top; 659 | var row = GetBottomAvailableRow(); 660 | if (row == -1) 661 | { 662 | return; 663 | } 664 | Grid.SetRow(grid, row); 665 | grid_Bottom.Children.Add(grid); 666 | 667 | 668 | //创建空转换动画 669 | TranslateTransform moveTransform = new TranslateTransform(); 670 | //创建动画 671 | Duration duration = new Duration(TimeSpan.FromSeconds(5)); 672 | DoubleAnimation myDoubleAnimationX = new DoubleAnimation(); 673 | myDoubleAnimationX.Duration = duration; 674 | //创建故事版 675 | Storyboard moveStoryboard = new Storyboard(); 676 | moveStoryboard.Duration = duration; 677 | moveStoryboard.Children.Add(myDoubleAnimationX); 678 | Storyboard.SetTarget(myDoubleAnimationX, moveTransform); 679 | //故事版加入动画 680 | Storyboard.SetTargetProperty(myDoubleAnimationX, "X"); 681 | topBottomStoryList.Add(moveStoryboard); 682 | 683 | moveStoryboard.Completed += new EventHandler((senders, obj) => 684 | { 685 | grid_Bottom.Children.Remove(grid); 686 | grid.Children.Clear(); 687 | grid = null; 688 | topBottomStoryList.Remove(moveStoryboard); 689 | moveStoryboard.Stop(); 690 | moveStoryboard = null; 691 | }); 692 | moveStoryboard.Begin(); 693 | } 694 | /// 695 | /// 添加定位弹幕 696 | /// 697 | /// 698 | public async Task AddPositionDanmu(DanmakuModel m) 699 | { 700 | var data = Newtonsoft.Json.JsonConvert.DeserializeObject(m.text); 701 | m.text = data[4].ToString().Replace("/n", "\r\n"); 702 | Grid grid = await CreateNewDanmuControl(m); ; 703 | var DanmakuFontFamilyFamily = data[data.Length - 2].ToString(); 704 | 705 | grid.Tag = m; 706 | grid.HorizontalAlignment = HorizontalAlignment.Left; 707 | grid.VerticalAlignment = VerticalAlignment.Center; 708 | 709 | double toX = 0; 710 | double toY = 0; 711 | 712 | double X = 0, Y = 0; 713 | double dur = 0; 714 | 715 | if (data.Length > 7) 716 | { 717 | X =data[0].ToDouble(); 718 | Y = data[1].ToDouble(); 719 | 720 | toX =data[7].ToDouble(); 721 | toY = data[8].ToDouble(); 722 | 723 | dur = data[10].ToDouble(); 724 | 725 | } 726 | else 727 | { 728 | toX = data[0].ToDouble(); 729 | toY =data[1].ToDouble(); 730 | } 731 | if (toX < 1 && toY < 1) 732 | { 733 | toX = this.ActualWidth * toX; 734 | toY = this.ActualHeight * toY; 735 | } 736 | if (X < 1 && Y < 1) 737 | { 738 | X = this.ActualWidth * X; 739 | Y = this.ActualHeight * Y; 740 | } 741 | 742 | if (data.Length >= 7) 743 | { 744 | var rotateZ = data[5].ToDouble(); 745 | var rotateY = data[6].ToDouble(); 746 | PlaneProjection projection = new PlaneProjection(); 747 | projection.RotationZ = -rotateZ; 748 | projection.RotationY = rotateY; 749 | grid.Projection = projection; 750 | } 751 | 752 | //Canvas.SetLeft(grid, toX); 753 | //Canvas.SetTop(grid, toY); 754 | 755 | canvas.Children.Add(grid); 756 | 757 | 758 | double dmDuration = data[3].ToDouble(); 759 | var opacitys = data[2].ToString().Split('-'); 760 | double opacityFrom = opacitys[0].ToDouble(); 761 | double opacityTo = opacitys[1].ToDouble(); 762 | 763 | //创建故事版 764 | Storyboard moveStoryboard = new Storyboard(); 765 | 766 | 767 | //if (X != toX || Y != toY) 768 | //{ 769 | Duration duration = new Duration(TimeSpan.FromMilliseconds(dur)); 770 | { 771 | DoubleAnimation myDoubleAnimationY = new DoubleAnimation(); 772 | myDoubleAnimationY.Duration = duration; 773 | myDoubleAnimationY.From = Y; 774 | myDoubleAnimationY.To = toY; 775 | 776 | 777 | Storyboard.SetTarget(myDoubleAnimationY, grid); 778 | Storyboard.SetTargetProperty(myDoubleAnimationY, "(Canvas.Top)"); 779 | moveStoryboard.Children.Add(myDoubleAnimationY); 780 | } 781 | { 782 | DoubleAnimation myDoubleAnimationX = new DoubleAnimation(); 783 | myDoubleAnimationX.Duration = duration; 784 | myDoubleAnimationX.From = X; 785 | myDoubleAnimationX.To = toX; 786 | Storyboard.SetTarget(myDoubleAnimationX, grid); 787 | Storyboard.SetTargetProperty(myDoubleAnimationX, "(Canvas.Left)"); 788 | moveStoryboard.Children.Add(myDoubleAnimationX); 789 | } 790 | //} 791 | //else 792 | //{ 793 | // Canvas.SetTop(grid,toY); 794 | // Canvas.SetLeft(grid,toX); 795 | //} 796 | 797 | //透明度动画 798 | DoubleAnimation opacityAnimation = new DoubleAnimation() 799 | { 800 | Duration = new Duration(TimeSpan.FromSeconds(dmDuration)), 801 | From = opacityFrom, 802 | To = opacityTo 803 | }; 804 | Storyboard.SetTarget(opacityAnimation, grid); 805 | Storyboard.SetTargetProperty(opacityAnimation, "Opacity"); 806 | moveStoryboard.Children.Add(opacityAnimation); 807 | 808 | 809 | 810 | positionStoryList.Add(moveStoryboard); 811 | 812 | moveStoryboard.Completed += new EventHandler((senders, obj) => 813 | { 814 | canvas.Children.Remove(grid); 815 | positionStoryList.Remove(moveStoryboard); 816 | }); 817 | moveStoryboard.Begin(); 818 | } 819 | #region 弹幕控制方法 820 | /// 821 | /// 暂停弹幕 822 | /// 823 | public void PauseDanmaku() 824 | { 825 | foreach (var item in topBottomStoryList) 826 | { 827 | item.Pause(); 828 | } 829 | foreach (var item in rollStoryList) 830 | { 831 | item.Pause(); 832 | } 833 | foreach (var item in positionStoryList) 834 | { 835 | item.Pause(); 836 | } 837 | } 838 | /// 839 | /// 继续弹幕 840 | /// 841 | public void ResumeDanmaku() 842 | { 843 | foreach (var item in topBottomStoryList) 844 | { 845 | item.Resume(); 846 | } 847 | foreach (var item in rollStoryList) 848 | { 849 | item.Resume(); 850 | } 851 | foreach (var item in positionStoryList) 852 | { 853 | item.Resume(); 854 | } 855 | } 856 | /// 857 | /// 移除指定弹幕 858 | /// 859 | /// 860 | public void Remove(DanmakuModel danmaku) 861 | { 862 | switch (danmaku.location) 863 | { 864 | case DanmakuLocation.Top: 865 | 866 | foreach (Grid item in grid_Top.Children) 867 | { 868 | if (item.Tag as DanmakuModel == danmaku) 869 | { 870 | grid_Top.Children.Remove(item); 871 | } 872 | } 873 | break; 874 | case DanmakuLocation.Bottom: 875 | foreach (Grid item in grid_Bottom.Children) 876 | { 877 | if (item.Tag as DanmakuModel == danmaku) 878 | { 879 | grid_Bottom.Children.Remove(item); 880 | } 881 | } 882 | break; 883 | case DanmakuLocation.Other: 884 | foreach (Grid item in grid_Scroll.Children) 885 | { 886 | if (item.Tag as DanmakuModel == danmaku) 887 | { 888 | grid_Scroll.Children.Remove(item); 889 | } 890 | } 891 | break; 892 | default: 893 | break; 894 | } 895 | } 896 | /// 897 | /// 清空弹幕 898 | /// 899 | public void ClearAll() 900 | { 901 | topBottomStoryList.Clear(); 902 | rollStoryList.Clear(); 903 | grid_Bottom.Children.Clear(); 904 | grid_Top.Children.Clear(); 905 | grid_Scroll.Children.Clear(); 906 | 907 | } 908 | 909 | /// 910 | /// 读取屏幕上的全部弹幕 911 | /// 912 | /// 类型 913 | /// 914 | public List GetDanmakus(DanmakuLocation? danmakuLocation = null) 915 | { 916 | List danmakus = new List(); 917 | if (danmakuLocation == null || danmakuLocation == DanmakuLocation.Top) 918 | { 919 | foreach (Grid item in grid_Top.Children) 920 | { 921 | danmakus.Add(item.Tag as DanmakuModel); 922 | } 923 | } 924 | if (danmakuLocation == null || danmakuLocation == DanmakuLocation.Bottom) 925 | { 926 | foreach (Grid item in grid_Bottom.Children) 927 | { 928 | danmakus.Add(item.Tag as DanmakuModel); 929 | } 930 | } 931 | if (danmakuLocation == null || danmakuLocation == DanmakuLocation.Scroll) 932 | { 933 | foreach (Grid item in grid_Scroll.Children) 934 | { 935 | danmakus.Add(item.Tag as DanmakuModel); 936 | } 937 | } 938 | return danmakus; 939 | } 940 | 941 | /// 942 | /// 隐藏弹幕 943 | /// 944 | /// 需要隐藏的位置 945 | public void HideDanmaku(DanmakuLocation location) 946 | { 947 | switch (location) 948 | { 949 | case DanmakuLocation.Scroll: 950 | grid_Scroll.Visibility = Visibility.Collapsed; 951 | break; 952 | case DanmakuLocation.Top: 953 | grid_Top.Visibility = Visibility.Collapsed; 954 | break; 955 | case DanmakuLocation.Bottom: 956 | grid_Bottom.Visibility = Visibility.Collapsed; 957 | break; 958 | default: 959 | break; 960 | } 961 | } 962 | 963 | /// 964 | /// 显示弹幕 965 | /// 966 | /// 需要显示的位置 967 | public void ShowDanmaku(DanmakuLocation location) 968 | { 969 | switch (location) 970 | { 971 | case DanmakuLocation.Scroll: 972 | grid_Scroll.Visibility = Visibility.Visible; 973 | break; 974 | case DanmakuLocation.Top: 975 | grid_Top.Visibility = Visibility.Visible; 976 | break; 977 | case DanmakuLocation.Bottom: 978 | grid_Bottom.Visibility = Visibility.Visible; 979 | break; 980 | default: 981 | break; 982 | } 983 | } 984 | #endregion 985 | } 986 | } 987 | --------------------------------------------------------------------------------