WPF+SkiaSharp实现自绘弹幕效果

SkiaSharp 自绘弹幕效果

框架使用.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);
        }
    }
}

实现效果

到此这篇关于WPF+SkiaSharp实现自绘弹幕效果的文章就介绍到这了,更多相关WPF SkiaSharp弹幕内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 基于WPF实现弹幕效果的示例代码

    WPF 实现弹幕效果 框架使用大于等于.NET40: Visual Studio 2022; 项目使用 MIT 开源许可协议: 此篇代码目的只是为了分享思路 实现基础弹幕一定是要使用Canvas比较简单,只需实现Left动画从右到左. 弹幕消息使用Border做弹幕背景. 内容使用TextBlock做消息文本展示. 当动画执行完成默认移除Canvas中的弹幕控件. 使用这种方式去加载弹幕GPU会占较高. 1) 准备BarrageExample.xaml如下: <UserControl x:Cla

  • WPF+SkiaSharp实现自绘拖曳小球

    目录 拖曳小球 Wpf 和 SkiaSharp 实现代码 效果如下 拖曳小球 WPF的拖曳效果,基本配置一下,就可以了,但是自绘的话,就得自己控制,按键点击,按键移动和按键松开的事件,与其配合达到目的. 这个效果实现了,其实也变相的实现了WPF里的拖动效果,这个效果用着还是很方便的. 但是代码,确十分的简单. Wpf 和 SkiaSharp 新建一个WPF项目,然后,Nuget包即可 要添加Nuget包 Install-Package SkiaSharp.Views.WPF -Version 2

  • .NET Core利用skiasharp文字头像生成方法教程(基于docker发布)

    一.问题背景 目前.NET Core下面针对于图像处理的库微软并没有集成,在.NET FrameWork下我们已经习惯使用System.Drawing类库做简单的图像处理,到了.NET Core下一脸懵逼的我,只能百度+谷歌看看有没啥解决方案,好在网上资料也多,.NET Core下的图像处理还是有些开源库的,我目前使用的其中一个:SkiaSharp,介绍反正大家自己网上找找都有,下面就用该库实现一个文字头像的小功能,话不多说了,来一起看看详细的介绍吧. 二.简单的设计要求 对于输入的名字得解析(

  • ASP.NET Core使用SkiaSharp实现验证码的示例代码

    前言 本文并没有实现一个完成的验证码样例,只是提供了在当前.NET Core 2.0下使用Drawing API的另一种思路,并以简单Demo的形式展示出来. Skia Skia是一个开源的二维图形库,提供各种常用的API,并可在多种软硬件平台上运行.谷歌Chrome浏览器.Chrome OS.安卓.火狐浏览器.火狐操作系统以及其它许多产品都使用它作为图形引擎. Skia由谷歌出资管理,任何人都可基于BSD免费软件许可证使用Skia.Skia开发团队致力于开发其核心部分, 并广泛采纳各方对于Sk

  • WPF+SkiaSharp实现自绘投篮小游戏

    目录 投篮小游戏 Wpf 和 SkiaSharp 弹球实体代码 (Ball.cs) 粒子花园核心类 (ParticleGarden.cs) 效果如下 总结 代码地址 投篮小游戏 规则,点击投篮目标点,就会有一个球沿着相关抛物线,然后,判断是否进入篮子里,其实就是一个矩形,直接是按照碰撞检测来的,碰到就算进去了,对其增加了一个分数统计等功能. Wpf 和 SkiaSharp 新建一个 WPF 项目,然后,Nuget 包即可 要添加 Nuget 包 Install-Package SkiaSharp

  • WPF+SkiaSharp实现自绘弹幕效果

    SkiaSharp 自绘弹幕效果 框架使用.NET60: Visual Studio 2022; 项目使用 MIT 开源许可协议: 接着上一篇 WPF 弹幕 上期有网友建议使用Skia实现弹幕. 弹幕消息使用SKElement做弹幕展现,然后在SKCanvas进行绘制弹幕. 由于需要绘制矩形与文本所以需要使用到SKBitmap进行绘制弹幕类. 创建SKBitmap设置宽(根据文本的长度定义宽度)与高度40. 创建对象SKCanvas并实例化的时候将SKBitmap传入,然后对SKCanvas进行

  • jQuery实现的弹幕效果完整实例

    本文实例讲述了jQuery实现的弹幕效果.分享给大家供大家参考,具体如下: 看视频的时候老是会出现一些弹幕,对于这个看着很高大上的特效,其实也不难实现. 先来看看运行效果: 下面将整个代码显示出来: <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>www.jb51.net弹幕</title> <st

  • Android实现自定义的弹幕效果

    一.效果图 先来看看效果图吧~~ 二.实现原理方案 1.自定义ViewGroup-XCDanmuView,继承RelativeLayout来实现,当然也可以继承其他三大布局类哈 2.初始化若干个TextView(弹幕的item View,这里以TextView 为例,当然也可以其他了~),然后通过addView添加到自定义View中 3.通过addView添加到XCDanmuView中,位置在坐标,为了实现 从屏幕外移动进来的效果 我们还需要修改添加进来TextView的位置,以从右向左移动方向

  • Android仿斗鱼直播的弹幕效果

    记得之前有位朋友在我的公众号里问过我,像直播的那种弹幕功能该如何实现?如今直播行业确实是非常火爆啊,大大小小的公司都要涉足一下直播的领域,用斗鱼的话来讲,现在就是千播之战.而弹幕则无疑是直播功能当中最为重要的一个功能之一,那么今天,我就带着大家一起来实现一个简单的Android端弹幕效果. 分析 首先我们来看一下斗鱼上的弹幕效果,如下图所示: 这是一个Dota2游戏直播的界面,我们可以看到,在游戏界面的上方有很多的弹幕,看直播的观众们就是在这里进行讨论的. 那么这样的一个界面该如何实现呢?其实并

  • 又一枚精彩的弹幕效果jQuery实现

    简易弹幕效果:将发布的内容随机显示在弹幕右侧,逐渐左移最后消失. 涉及知识点:val().random().height().css().append().remove()等,主要是元素的操作 html代码: <a href="#">弹幕技术</a> <div class="mask"> <a href="#" class="button">X</a> </di

  • 终于实现了!精彩的jquery弹幕效果

    本文实例为大家分享了jquery弹幕效果,供大家参考,具体内容如下 页面效果如下: html页面如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"

  • JQuery和HTML5 Canvas实现弹幕效果

    JQuery和HTML5 Canvas两种方法实现弹幕效果: 方法一,JQuery实现. 源码: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" cont

随机推荐