我的编程空间,编程开发者的网络收藏夹
学习永远不晚

WPF+SkiaSharp实现自绘弹幕效果

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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弹幕内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

WPF+SkiaSharp实现自绘弹幕效果

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

这篇文章主要介绍了如何利用WPF+SkiaSharp实现自绘投篮小游戏。此案例主要是针对光线投影法碰撞检测功能的示例,顺便做成了一个小游戏,很简单,但是,效果却很不错,感兴趣的可以动手尝试一下
2022-11-13

Android实现自定义的弹幕效果

一、效果图 先来看看效果图吧~~二、实现原理方案 1、自定义ViewGroup-XCDanmuView,继承RelativeLayout来实现,当然也可以继承其他三大布局类哈 2、初始化若干个TextView(弹幕的item View,这里
2022-06-06

前端html如何实现弹幕效果

这篇文章给大家分享的是有关前端html如何实现弹幕效果的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。之前在一个移动端的抽奖页面中,在抽奖结果的展示窗口需要弹幕轮播显示,之前踩过一些小坑,现在总结一下前端弹幕效果的
2023-06-09

怎么使用Android Flutter实现弹幕效果

本篇内容介绍了“怎么使用Android Flutter实现弹幕效果”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!前言需求要点如下:弹幕行数为
2023-07-02

Android中怎么利用EasyBarrage实现一个弹幕效果

Android中怎么利用EasyBarrage实现一个弹幕效果,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。概述EasyBarrage是Android平台的一种轻量级弹幕效
2023-05-30

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录