├── 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 |
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 | 
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