SkiaSharp 自绘弹幕效果

 SkiaSharp 自绘弹幕效果

控件名:SkiaSharpBarrage

作者: 驚鏵

原文链接:    https://github.com/yanjinhuagood/SkiaSharpBarrage

  • 框架使用.NET60

  • Visual Studio 2022;

  • 项目使用 MIT 开源许可协议;

  • 接着上一篇 WPF 弹幕

  • 上期有网友建议使用Skia实现弹幕。

    • 弹幕消息使用SKElement做弹幕展现,然后在SKCanvas进行绘制弹幕。

    • 由于需要绘制矩形与文本所以需要使用到SKBitmap进行绘制弹幕类。

    • 创建SKBitmap设置宽(根据文本的长度定义宽度)与高度40

    • 创建对象SKCanvas并实例化的时候将SKBitmap传入,然后对SKCanvas进行绘制背景DrawRoundRect 与文本DrawText ,使用属性记录XY的值方便在动画的时候让弹幕动起来。

    • BarrageRender的时候进行绘制弹幕图片DrawImage(SKBitmap,x,y)

    • 弹幕每次移动多少值 等于SKCanvas的宽除以弹幕的

    • 当弹幕移动Move()时如超过-Width则通过out返回GUID 就移除弹幕对象。

1) 准备 MsgInfo 弹幕消息类如下:

using System;
using SkiaSharp;namespace SkiaSharpBarrage
{/// <summary>///     msg info/// </summary>public class MsgInfo{private string _msg;public string GUID;public MsgInfo(string msg, SKTypeface _font, float windowsWidth){_msg = msg;var _random = new Random();var skColor = new SKColor((byte) _random.Next(1, 255),(byte) _random.Next(1, 255), (byte) _random.Next(1, 233));using var paint = new SKPaint{Color = skColor,Style = SKPaintStyle.Fill,IsAntialias = true,StrokeWidth = 2};paint.Shader = SKShader.CreateLinearGradient(new SKPoint(0, 0),new SKPoint(1, 1),new[] {SKColors.Transparent, skColor},new float[] {0, 1},SKShaderTileMode.Repeat);using var paintText = new SKPaint{Color = SKColors.White,IsAntialias = true,Typeface = _font,TextSize = 24};var textBounds = new SKRect();paintText.MeasureText(msg, ref textBounds);var width = textBounds.Width + 100;SKImage skImage;using (var bitmap = new SKBitmap((int) width, 40, true))using (var canvas = new SKCanvas(bitmap)){canvas.DrawRoundRect(0, 0, width, 40, 20, 20, paint);canvas.DrawText(msg, width / 2 - textBounds.Width / 2, bitmap.Height / 2 + textBounds.Height / 2,paintText);var image = SKImage.FromBitmap(bitmap);skImage = image;}SKImage = skImage;Width = width;X = windowsWidth + Width;CanvasWidth = windowsWidth;CostTime = TimeSpan.FromMilliseconds(Width);GUID = Guid.NewGuid().ToString("N");}public float X { get; set; }public float Y { get; set; }public float Width { get; set; }public float CanvasWidth { get; set; }public SKImage SKImage { get; set; }public float MoveNum => CanvasWidth / (float) CostTime.TotalMilliseconds;public TimeSpan CostTime { get; set; }/// <summary>///     定时调用,移动指定距离/// </summary>public void Move(out string guid){guid = string.Empty;X = X - MoveNum;if (X <= -Width)guid = GUID;}}
}

2) 新建 Barrage.cs 类如下:

using System.Collections.Generic;
using System.Linq;
using SkiaSharp;namespace SkiaSharpBarrage
{public class Barrage{private readonly SKTypeface _font;private readonly List<MsgInfo> _MsgInfo;private int _num, _index;private double _right, _top;private float _width;private readonly float _height;public Barrage(SKTypeface font, float width, float height, List<string> strList){_width = width;_height = height;_font = font;_num = (int) height / 40;_MsgInfo = new List<MsgInfo>();foreach (var item in strList) BuildMsgInfo(item);}private void BuildMsgInfo(string text){_index++;if (_right != 0)_width = (float) _right;var model = new MsgInfo(text, _font, _width);_right = _right == 0 ? _height + model.Width : _right;var y = _height - 40;_top = _top + 40 >= y ? 40 : _top;model.Y = (float) _top;_MsgInfo.Add(model);_top += 60;}public void AddBarrage(string text){BuildMsgInfo(text);}public void Render(SKCanvas canvas, SKTypeface font, int width, int height, List<string> strList){for (var i = 0; i < _MsgInfo.Count; i++){var info = _MsgInfo[i];var guid = string.Empty;info.Move(out guid);if (!string.IsNullOrEmpty(guid)){var model = _MsgInfo.FirstOrDefault(x => x.GUID == guid);_MsgInfo.Remove(model);}canvas.DrawImage(info.SKImage, info.X, info.Y);}}}
}

3) MainWindow.xaml.cs 如下:

<wpfdev:Window x:Class="SkiaSharpBarrage.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers"xmlns:skia="clr-namespace:SkiaSharp.Views.WPF;assembly=SkiaSharp.Views.WPF"mc:Ignorable="d" WindowStartupLocation="CenterScreen"ResizeMode="CanMinimize"Title="SkiaSharpBarrage - 弹幕篇" Height="450" Width="800"><Grid Margin="4"><Grid.RowDefinitions><RowDefinition /><RowDefinition Height="Auto" /></Grid.RowDefinitions><MediaElement Stretch="Uniform" Grid.RowSpan="2"Name="myMediaElement" /><skia:SKElement x:Name="skElement" /><Grid Grid.Row="1" Name="MyGrid"><Grid.ColumnDefinitions><ColumnDefinition /><ColumnDefinition Width="Auto" /></Grid.ColumnDefinitions><TextBox wpfdev:ElementHelper.IsWatermark="True"x:Name="tbBarrage"wpfdev:ElementHelper.Watermark="请弹幕内容" /><Button Grid.Column="1" Style="{StaticResource PrimaryButton}"Content="发射弹幕" Margin="4,0,0,0"Click="ButtonBase_OnClick" /></Grid></Grid>
</wpfdev:Window>

3) 逻辑 MainWindow.xaml.cs 如下:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using SkiaSharp;
using SkiaSharp.Views.Desktop;namespace SkiaSharpBarrage
{/// <summary>///     Interaction logic for MainWindow.xaml/// </summary>public partial class MainWindow{private readonly Barrage _barrage;private readonly SKTypeface _font;private readonly List<string> list = new List<string>();public MainWindow(){list.Add("2333");list.Add("测试弹幕公众号:WPF开发者");list.Add("很难开心");list.Add("LOL~");list.Add("青春记忆");list.Add("bing");list.Add("Microsoft");InitializeComponent();var index = SKFontManager.Default.FontFamilies.ToList().IndexOf("微软雅黑");_font = SKFontManager.Default.GetFontStyles(index).CreateTypeface(0);_barrage = new Barrage(_font, (float) Width, (float) Height - (float) MyGrid.ActualHeight, list);skElement.PaintSurface += SkElement_PaintSurface;Loaded += delegate{myMediaElement.Source =new Uri(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Leagueoflegends.mp4"));};_ = Task.Run(() =>{try{while (true){Dispatcher.Invoke(() => { skElement.InvalidateVisual(); });_ = SpinWait.SpinUntil(() => false, 1000 / 60); //每秒60帧}}catch (Exception e){}});}private void SkElement_PaintSurface(object? sender, SKPaintSurfaceEventArgs e){var canvas = e.Surface.Canvas;canvas.Clear();_barrage.Render(canvas, _font, e.Info.Width, e.Info.Height, list);}private void ButtonBase_OnClick(object sender, RoutedEventArgs e){_barrage.AddBarrage(tbBarrage.Text);}}
}
249f047005f0a31ecdf3d8c7be90ccd7.gif

Github|SkiaSharpBarrage[1]
Gitee|SkiaSharpBarrage[2]

参考资料

[1]

Github|SkiaSharpBarrage: https://github.com/yanjinhuagood/SkiaSharpBarrage

[2]

Gitee|SkiaSharpBarrage: https://gitee.com/yanjinhua/SkiaSharpBarrage

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/282473.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript中this指向

一.重点来了&#xff0c;this指向问题&#xff1a;1.this指向之普通函数。 2.this指向之对象 3.this指向之构造函数 4.this指向之&#xff08;call,apply&#xff09;动态更改this指向。 二.具体分析如下 1.普通函数 // 第23行的调用者为null,this指向也为null,// 所以这时js把…

提交Form表单,submit之前做js判断处理

效果:在点击提交按钮时,首先进行js判断, 如果不符合条件,则alert出提示信息,并return false. 主要点就在于给form表单添加一个onsubmit事件. 在onsubmit事件中定义的函数里进行js验证处理.代码 : <!DOCTYPE html> <html lang"en"> <head><meta …

如何在Windows上一键部署PaddleOCR的WebAPI服务

PaddleOCR旨在打造一套丰富、领先、且实用的OCR工具库&#xff0c;助力开发者训练出更好的模型&#xff0c;并应用落地。官方开源项目地址&#xff1a;https://github.com/PaddlePaddle/PaddleOCR一定会有小伙伴们看完不知道如何部署与应用&#xff0c;怎么才能融入到自己的产品…

微软为 Visual Studio 扩展添加对 Arm64 的支持

微软在 6 月份推出了支持 Arm64 架构的 Visual Studio&#xff0c;这是第一个原生支持在基于 Arm 的处理器上构建和调试 Arm64 应用程序的 Visual Studio 版本。近日&#xff0c;他们宣布为 Visual Studio 扩展也添加了对 Arm64 的支持&#xff0c;因此开发者可在 Arm64 Visual…

WIN10 查看已经连接的wifi的密码

命令行: 1. 显示以前连接过的wifi2. 将wifi配置存入文件中3. 查看刚刚保存的wifi配置的文件这样,我们就可以看到连接的wifi名称和wifi密码了.

微软与 Canonical 合作,将 systemd 引入 WSL

微软和 Canonical 联合宣布&#xff0c;systemd 现在可以在 Windows Subsystem for Linux&#xff08;WSL2&#xff09;中运行了&#xff0c;此举可以让用户在 Windows 设备上获得更加全面的 Linux 体验。systemd 的作者 Lennart Poettering 在 7 月份离开红帽并加入了微软&…

.NET 反向代理-YARP 根据域名转发

编者&#xff1a;fastgithub 就是基于YARP使用域名做转发逻辑的。 前段时间发布过一个关于 YARP 的简单介绍&#xff0c;感兴趣的小伙伴恭请移步看看 .NET 反向代理-YARP 作为反向代理&#xff0c;必不可少的当然是根据域名代理转发啦&#xff0c;毫无疑问&#xff0c;YARP…

第一个python小游戏

guess int(input("猜一猜宝宝心目中的数字是多少:")) secret 8 while guess !secret:guess int(input("哎呀猜错了,重新猜一猜宝宝心目中的数字是多少:"))if guess secret:print("你真厉害,居然猜对了")print("哼,猜对了也不给你奖励&q…

理论实践:循序渐进理解AWR细致入微分析性能报告

1. AWR 概述 Automatic Workload Repository(AWR) 是10g引入的一个重要组件。在里面存贮着近期一段时间内&#xff08;默认是7天&#xff09;数据库活动状态的详细信息。 AWR 报告是对 AWR 视图进行查询而得到的一份自动生成的报告。可以通过下面的脚本手工得到一份 AWR 报告。…

mysql sql语句书写之面试部分

要求一 :查询时,将用户的手机号码(比如1331234567)显示为133***4567 这是在交流群里看到别人发的一个面试题,我本人非常反感直接在查询时进行处理数据的,查询出来再处理不好吗,但是面试题要求是这样. 这里,简单的写了两个表关联查询,然后把手机号码进行处理显示出来select a.ui…

Redis --数据类型 [1]

一 string 类型 (最简单常用的类型) string是redis最基本的类型&#xff0c;你可以理解成与Memcached一模一样的类型&#xff0c;一个key对应一个value。二 Hash类型(哈希) Redis hash是一个string类型的field和value的映射表&#xff0c;hash特别适合用于存储对象。三 List(列…

KestrelServer详解[3]: 自定义一个迷你版的KestrelServer

和所有的服务器一样&#xff0c;KestrelServer最终需要解决的是网络传输的问题。在《KestrelServer详解[2]: 网络连接是如何创建的&#xff1f;》&#xff0c;我们介绍了KestrelServer如何利用连接接听器的建立网络连接&#xff0c;并再次基础上演示了如何直接利用建立的连接接…

c# 文件下载

这样的下载方式 减少服务器的压力&#xff0c; 还有一种省懒劲的方式&#xff1a;后端在iis上配置一个虚拟目录&#xff0c;然后让前端自己拼url地址下载&#xff0c; 这个东西是给后期其他工作人员埋坑&#xff0c;哈哈。 本帖原文转自与 农码一生转载于:https://www.cnbl…

Redis -- 基础操作 [2]

一 获取redis当前数据库符合条件键名 [keys pattern]二 设置string形式key-value [set key value]三 获取存储在指定 key 中字符串的子字符串 [GETRANGE KEY start end]四 删除指定键值对 [del key]五 为给定key设置过期时间 [Expire KEY SECONDS]注: Expireat KEY TIMESTAMP 同…

Centos7作为VNCserver,本地使用VNCViewer连接

1.概念 VNC是一个远程连接工具 VNC is used to display an X windows session running on another computer. Unlike a remote X connection, the xserver is running on the remote computer, not on your local workstation. Your workstation ( Linux or Windows ) is only …

在URL中实现简易的WebAPI验签

本文主要介绍一种与微信公众平台对接方式类似的&#xff0c;为 AspNetCore 提供的一种简易的 WebAPI 签名验证中间件。本文相关源码和案例已开源&#xff0c;地址&#xff1a;https://github.com/sangyuxiaowu/SignAuthorization原理说明简易的 API url 签名验证中间件&#xf…

Redis -- Hash(哈希) [3]

Redis Hash 是一个string类型的field和value的 映射表 &#xff0c;hash特别适合用于存储对象。 注 : Redis 中每个 hash 可以存储 232 - 1 键值对&#xff08;40多亿&#xff09;。 比如这样:注:在此,首先推荐一款redis可视化工具 https://redisdesktop.com/download , 是非常…

HBuilder 打包流程

1.运行HBuilder---百度搜索HBuilder&#xff0c;官网下载安装包&#xff0c;解压&#xff0c;运行HBuilder.exe。注册账号&#xff0c;并登陆 2.新建app---在左边右键&#xff0c;选择新建APP&#xff0c;或者&#xff0c;点击中间的新建app 3.在弹出的窗口&#xff0c;填入应用…

Python3——字典

Python 字典(Dictionary) 字典是另一种可变容器模型&#xff0c;且可存储任意类型对象。 字典的每个键值(key>value)对用冒号(:)分割&#xff0c;每个对之间用逗号(,)分割&#xff0c;整个字典包括在花括号({})中 定义字典 d {} d {key1 : value1, key2 : value2 } d di…