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
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...