SkiaSharp 之 WPF 自繪 五環彈動球(案例版)

来源:https://www.cnblogs.com/kesshei/archive/2022/08/01/16538440.html
-Advertisement-
Play Games

此案例基於拖曳和彈動球兩個技術功能實現,如有不懂的可以參考之前的相關文章,屬於遞進式教程。 五環彈動球 好吧,名字是我起的,其實,你可以任意個球進行聯動彈動,效果還是很不錯的,有很多前端都是基於這個特效,可以搞出一些很有科技感的效果出來。 Wpf 和 SkiaSharp 新建一個WPF項目,然後,N ...


此案例基於拖曳和彈動球兩個技術功能實現,如有不懂的可以參考之前的相關文章,屬於遞進式教程。

五環彈動球

好吧,名字是我起的,其實,你可以任意個球進行聯動彈動,效果還是很不錯的,有很多前端都是基於這個特效,可以搞出一些很有科技感的效果出來。

Wpf 和 SkiaSharp

新建一個WPF項目,然後,Nuget包即可
要添加Nuget包

Install-Package SkiaSharp.Views.WPF -Version 2.88.0

其中核心邏輯是這部分,會以我設置的60FPS來刷新當前的畫板。

skContainer.PaintSurface += SkContainer_PaintSurface;
_ = Task.Run(() =>
{
    while (true)
    {
        try
        {
            Dispatcher.Invoke(() =>
            {
                skContainer.InvalidateVisual();
            });
            _ = SpinWait.SpinUntil(() => false, 1000 / 60);//每秒60幀
        }
        catch
        {
            break;
        }
    }
});

彈球實體代碼 (Ball.cs)

public class Ball
{
    public double X { get; set; }
    public double Y { get; set; }
    public double VX { get; set; }
    public double VY { get; set; }
    public int Radius { get; set; }
    public bool Dragged { get; set; } = false;
    public SKColor sKColor { get; set; } = SKColors.Blue;
    public bool CheckPoint(SKPoint sKPoint)
    {
        var d = Math.Sqrt(Math.Pow(sKPoint.X - X, 2) + Math.Pow(sKPoint.Y - Y, 2));
        return this.Radius >= d;
    }
}

五環彈動核心類 (FiveRings.cs)

/// <summary>
/// 五環彈球
/// </summary>
public class FiveRings
{
    public SKPoint centerPoint;
    public int Radius = 0;
    public int BallLength = 8;

    public double TargetX;
    public double Spring = 0.03;
    public double SpringLength = 200;
    public double Friction = 0.95;
    public List<Ball>? Balls;
    public Ball? draggedBall;
    public void init(SKCanvas canvas, SKTypeface Font, int Width, int Height)
    {
        if (Balls == null)
        {
            Balls = new List<Ball>();
            for (int i = 0; i < BallLength; i++)
            {
                Random random = new Random((int)DateTime.Now.Ticks);
                Balls.Add(new Ball()
                {
                    X = random.Next(50, Width - 50),
                    Y = random.Next(50, Height - 50),
                    Radius = this.Radius
                });
            }
        }
    }
    /// <summary>
    /// 渲染
    /// </summary>
    public void Render(SKCanvas canvas, SKTypeface Font, int Width, int Height)
    {
        centerPoint = new SKPoint(Width / 2, Height / 2);
        this.Radius = 20;
        this.TargetX = Width / 2;
        init(canvas, Font, Width, Height);
        canvas.Clear(SKColors.White);


        //劃線
        using var LinePaint = new SKPaint
        {
            Color = SKColors.Green,
            Style = SKPaintStyle.Fill,
            StrokeWidth = 3,
            IsStroke = true,
            StrokeCap = SKStrokeCap.Round,
            IsAntialias = true
        };
        SKPath path = null;
        foreach (var item in Balls)
        {
            if (path == null)
            {
                path = new SKPath();
                path.MoveTo((float)item.X, (float)item.Y);
            }
            else
            {
                path.LineTo((float)item.X, (float)item.Y);
            }
        }
        path.Close();
        canvas.DrawPath(path, LinePaint);


        foreach (var item in Balls)
        {
            if (!item.Dragged)
            {
                foreach (var ball in Balls.Where(t => t != item).ToList())
                {
                    SpringTo(item, ball);
                }
            }
            DrawCircle(canvas, item);
        }

        using var paint = new SKPaint
        {
            Color = SKColors.Blue,
            IsAntialias = true,
            Typeface = Font,
            TextSize = 24
        };
        string by = $"by 藍創精英團隊";
        canvas.DrawText(by, 600, 400, paint);

    }
    /// <summary>
    /// 畫一個圓
    /// </summary>
    public void DrawCircle(SKCanvas canvas, Ball ball)
    {
        using var paint = new SKPaint
        {
            Color = SKColors.Blue,
            Style = SKPaintStyle.Fill,
            IsAntialias = true,
            StrokeWidth = 2
        };
        canvas.DrawCircle((float)ball.X, (float)ball.Y, ball.Radius, paint);
    }
    public void MouseMove(SKPoint sKPoint)
    {
        if (draggedBall != null)
        {
            draggedBall.X = sKPoint.X;
            draggedBall.Y = sKPoint.Y;
        }
    }
    public void MouseDown(SKPoint sKPoint)
    {
        foreach (var item in Balls)
        {
            if (item.CheckPoint(sKPoint))
            {
                item.Dragged = true;
                draggedBall = item;
            }
            else
            {
                item.Dragged = false;
            }
        }
    }
    public void MouseUp(SKPoint sKPoint)
    {
        draggedBall = null;
        foreach (var item in Balls)
        {
            item.Dragged = false;
        }
    }
    public void SpringTo(Ball b1, Ball b2)
    {
        var dx = b2.X - b1.X;
        var dy = b2.Y - b1.Y;
        var angle = Math.Atan2(dy, dx);
        var targetX = b2.X - SpringLength * Math.Cos(angle);
        var targetY = b2.Y - SpringLength * Math.Sin(angle);

        b1.VX += (targetX - b1.X) * Spring;
        b1.VY += (targetY - b1.Y) * Spring;

        b1.VX *= Friction;
        b1.VY *= Friction;

        b1.X += b1.VX;
        b1.Y += b1.VY;
    }
}

效果如下:

這個特效用的好,也能產生一些神奇的效果。

總結

這次是結合拖曳和彈動效果實現的綜合案例,效果還是很不錯的,之前也沒想到原來還可以這樣玩,拓展了玩法啊。

代碼地址

https://github.com/kesshei/WPFSkiaFiveRingsDemo.git

https://gitee.com/kesshei/WPFSkiaFiveRingsDemo.git

一鍵三連呦!,感謝大佬的支持,您的支持就是我的動力!

版權

藍創精英團隊(公眾號同名,CSDN同名,CNBlogs同名)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • indexOf和subString取值走向圖;首先簡單介紹這者的作用,具體可以看官方API 1.indexOf的作用: 就是獲取某個字元串的其中具體一個字元的位置 2.subString的作用: 就是大家熟悉的切割,從那裡到那裡比如,從第一個到第五個等;當這個方法是重載的,不只有我說的這個方法;具體 ...
  • 明敏 曉查 發自 凹非寺 量子位 報道 | 公眾號 QbitAI 程式 bug 也能負負得正嗎? 還真可以。 比如程式員們再熟悉不過的排序演算法,通過兩個“bug”居然能歪打正著,實在令人匪夷所思。 請看這位程式員寫的數組升序排序代碼: for i = 1 to n do for j = 1 to n ...
  • 基礎確認:HTML、CSS、JavaScript AJAX可以: 不刷新頁面更新網頁 在頁面載入後從伺服器請求數據 在頁面載入後從伺服器接收數據 在後臺向伺服器發送數據 Ajax 的核心是 XMLHttpRequest 對象,用於和伺服器交換數據。 xmlhttp.open("GET","ajax_ ...
  • 多商戶商城系統,也稱為B2B2C(BBC)平臺電商模式多商家商城系統。可以快速幫助企業搭建類似拼多多/京東/天貓/淘寶的綜合商城。 多商戶商城系統支持商家入駐加盟,同時滿足平臺自營、旗艦店等多種經營方式。平臺可以通過收取商家入駐費,訂單交易服務費,提現手續費,簡訊通道費等多手段方式,實現整體盈利。 ...
  • 3 hashCode的內幕 tips:面試常問/常用/常出錯 hashCode到底是什麼?是不是對象的記憶體地址? 1) 直接用記憶體地址? 目標:通過一個Demo驗證這個hasCode到底是不是記憶體地址 public native int hashCode(); com.hashcode.HashCo ...
  • 函數 封裝功能,提高應用的模塊性和代碼重用率 1. 定義函數 1.1 函數內的第一條語句是字元串時,該字元串就是文檔字元串(docstring) def my_fun(): '''我是文檔字元串 函數內第一句是字元串時 該字元串就是文檔字元串 ''' pass print(my_fun.__doc_ ...
  • 前言 做了幾道關於defer的測試題,嚇了一大跳,感覺自己之前的理解有些問題,所以寫下這篇博客,加深下印象。 正文: 多個defer的執行順序: 先進後出,類似於棧的特性。 下麵我們來測試下: 1.defer 與 panic: func deferAndPanic() { defer func() ...
  • 前言 接著上周寫的截圖控制項繼續更新 繪製箭頭。 1.WPF實現截屏「仿微信」 2.WPF 實現截屏控制項之移動(二)「仿微信」 3.WPF 截圖控制項之伸縮(三) 「仿微信」 4.WPF 截圖控制項之繪製方框與橢圓(四) 「仿微信」 正文 一、首先接著ScreenCut繼續發電。 1)繪製箭頭因為需要只修 ...
一周排行
    -Advertisement-
    Play Games
  • Github / Gitee QQ群(1群) : 813100564 / QQ群(2群) : 579033769 視頻教學 介紹 MiniWord .NET Word模板引擎,藉由Word模板和數據簡單、快速生成文件。 Getting Started 安裝 nuget link : https:// ...
  • Array.Sort Array類中相當實用的我認為是Sort方法,相比起冗長的冒泡排序,它的出現讓排序更加的簡化 結果如下: 還可以聲明一個靜態方法用來專門調用指定數組排序,從名為 array 的一維數組中 a 索引處開始,到 b 元素 從小到大排序。 註意: a + b 不能大於 array 的 ...
  • 前言 在上一篇文章CLR類型系統概述里提到,當運行時掛起時, 垃圾回收會執行堆棧遍歷器(stack walker)去拿到堆棧上值類型的大小和堆棧根。這裡我們來翻譯BotR里一篇專門介紹Stackwalking的文章,希望能加深理解。 順便說一句,StackWalker在中文里似乎還沒有統一的翻譯,J ...
  • 使用過 nginx 的小伙伴應該都知道,這個中間件是可以設置跨域的,作為今天的主角,同樣的 反向代理中間件的 YARP 毫無意外也支持了跨域請求設置。 有些小伙伴可能會問了,怎樣才算是跨域呢? 在 HTML 中,一些標簽,例如 img、a 等,還有我們非常熟悉的 Ajax,都是可以指向非本站的資源的 ...
  • 什麼是Git Git 是一個開源的分散式版本控制系統,用於敏捷高效地處理任何或小或大的項目。 Git 是 Linus Torvalds 為了幫助管理 Linux 內核開發而開發的一個開放源碼的版本控制軟體。 Git 與常用的版本控制工具 CVS, Subversion 等不同,它採用了分散式版本庫的 ...
  • 首先CR3是什麼,CR3是一個寄存器,該寄存器內保存有頁目錄表物理地址(PDBR地址),其實CR3內部存放的就是頁目錄表的記憶體基地址,運用CR3切換可實現對特定進程記憶體地址的強制讀寫操作,此類讀寫屬於有痕讀寫,多數驅動保護都會將這個地址改為無效,此時CR3讀寫就失效了,當然如果能找到CR3的正確地址... ...
  • 說明 onlyoffice為一款開源的office線上編輯組件,提供word/excel/ppt編輯保存操作 以下操作均基於centos8系統,officeonly鏡像版本7.1.2.23 鏡像下載地址:https://yunpan.360.cn/surl_y87CKKcPdY4 (提取碼:1f92 ...
  • 二叉樹查找指定的節點 前序查找的思路 1.先判斷當前節點的no是否等於要查找的 2.如果是相等,則返回當前節點 3.如果不等,則判斷當前節點的左子節點是否為空,如果不為空,則遞歸前序查找 4.如果左遞歸前序查找,找到節點,則返回,否繼續判斷,當前的節點的右子節點是否為空,如果不為空,則繼續向右遞歸前 ...
  • ##Invalid bound statement (not found)出現原因和解決方法 ###前言: 想必各位小伙伴在碼路上經常會碰到奇奇怪怪的事情,比如出現Invalid bound statement (not found),那今天我就來分析以下出現此問題的原因。 其實出現這個問題實質就是 ...
  • ###一、背景知識 爬蟲的本質就是一個socket客戶端與服務端的通信過程,如果我們有多個url待爬取,只用一個線程且採用串列的方式執行,那隻能等待爬取一個結束後才能繼續下一個,效率會非常低。 需要強調的是:對於單線程下串列N個任務,並不完全等同於低效,如果這N個任務都是純計算的任務,那麼該線程對c ...