三角函數與緩入緩出動畫及C#實現(圖文講解)

来源:https://www.cnblogs.com/lesliexin/archive/2020/06/20/13167978.html
-Advertisement-
Play Games

日常經常能看到緩入緩出的動畫效果,如: 1,帶緩入緩出效果的滾動條: 2,帶緩入緩出效果的呼吸燈: 像上面這種效果,就是用到了三角函數相關的知識,下麵將從頭開始一步步去講解如何實現這種效果。 一、基礎知識 (一)三角函數 常用的三角函數有正弦函數(sin)、餘弦函數(cos)、正切函數(tan)。在 ...



日常經常能看到緩入緩出的動畫效果,如:

1,帶緩入緩出效果的滾動條:

2,帶緩入緩出效果的呼吸燈:

像上面這種效果,就是用到了三角函數相關的知識,下麵將從頭開始一步步去講解如何實現這種效果。


 

 

一、基礎知識

(一)三角函數

常用的三角函數有正弦函數(sin)、餘弦函數(cos)、正切函數(tan)。在動畫效果中常用的是正弦函數和餘弦函數,由於兩者可以互相轉化,所以本文將以正弦函數來進行講解。

如下圖所示直角三角形ABC:

則:

sin(A)=a/c

即:角A的正弦值=角A的對邊/斜邊

(二)正弦曲線

將三角函數與動畫橋接起來的便是三角函數曲線。

以正弦函數為例,其正弦曲線公式為:y = A*sin(B*x + C) + D

其中y、x分別是縱坐標、橫坐標。

1,在預設狀態時,即:y=sin(x) 時,其曲線如下圖所示:

2,正弦曲線公式中的參數 “A” 控制曲線的振幅,A 值越大,振幅越大,A 值越小,振幅越小。

如:y=2*sin(x),其曲線如下圖所示(藍線為 y=sin(x)):

3,參數 “B" 控控制曲線的周期,B 值越大,那麼周期越短,B 值越小,周期越長。

如:y=sin(2x),其曲線如下圖所示(藍線為 y=sin(x)):

4,參數 “C" 控控制曲線左右移動,C 值為正數,曲線左移,C 值為負數,曲線右移;

如:y=sin(x+1),其曲線如下圖所示(藍線為 y=sin(x)):

5,參數 “D" 控控制曲線上下移動。D 值為正數,曲線上移,D 值為負數,曲線下移;

如:y=sin(x)+1,其曲線如下圖所示(藍線為 y=sin(x)):

(三)角度與弧度

因為在使用代碼去計算正弦值時,其單位一般是弧度,像C#中的”Math.Sin()“函數,而直觀的效果卻是角度,所以需要講解一下角度與弧度。

1,角度

定義:兩條射線從圓心向圓周射出,形成一個夾角和夾角正對的一段弧。當這段弧長正好等於圓周長的360分之中的一個時,兩條射線的夾角的大小為1度。

示意圖如下:

2,弧度

定義:弧度:兩條射線從圓心向圓周射出,形成一個夾角和夾角正對的一段弧。當這段弧長正好等於圓的半徑時,兩條射線的夾角大小為1弧度。

示意圖如下:

其中:AB=OA=r

3,角度與弧度的差別

最基本的差別在於角所對的弧長大小不同。度的是等於圓周長的360分之中的一個,而弧度的是等於半徑。

4,角度與弧度的轉換

因為:弧度角=弧長/半徑

所以:

a,周角(360度)=周長/半徑=2πr/r=2π

b,平角(180度)=π

由b可知:180度=π弧度

所以:

c,1度=π/180 弧度(≈0.017453弧度)
d,1弧度=180/π 度(≈57.3度)

可得轉換公式:

弧度=度*π/180
度=弧度*180/π


 

三、動畫實現

實現的思路簡單而言便是利用正弦曲線的”y“和”x“值的變化。對於緩入緩出動畫,就是速度的變化。

速度的定義和公式:速度在數值上等於物體運動的位移跟發生這段位移所用的時間的比值。速度的計算公式為v=Δs/Δt

控制速度,無非是”距離“與”時間“這兩個量的變化。

在實現應用中,往往不會同時變化兩個量,而是固定一個量,變化一個量。

在實際程式實現時,一般是固定“時間”,只變化“距離”。此處的”時間“可以理解為”時間間隔“。即在時間間隔不變的情況下,需要考慮每個時間間隔內運行的距離。

那麼在正弦曲線上的體現便是等x間隔下,y的取值。

(一)簡單實現

(1)實現思路:

1,通過y=sin(x)的曲線可知:y值的範圍是(-1~+1)

2,將曲線上移,上移距離為1,即:y=sin(x)+1,此時y值的範圍:(0~2)

3,為使y值範圍變為(0~1),對函數除2,即:y=(sin(x)+1)/2

如圖(藍線為 y=sin(x)):

4,將y值乘以緩入緩出動畫的擺動距離

(2)C#實現:

1,控制項佈局及屬性

2,核心代碼

 1 void pShowD()
 2 {
 3     //i是度數,不是弧度
 4     int i = 0;
 5     //移動距離要減去滑塊本身的寬度
 6     double dMoveDistance = panel_Board.Width - panel_Slider.Width;
 7     while (true)
 8     {
 9         i++;
10         if (i > 360)
11         {
12             //一個周期是360度
13             i = 0;
14         }
15         //固定時間間隔
16         Thread.Sleep(10);
17         //通過公式:弧度=度*π/180,將度數i轉為Math.Sin()所需要的弧度數
18         double dz = dMoveDistance * (1 + Math.Sin(i * Math.PI / 180)) / 2;
19         pSetLeft(Convert.ToInt32(dz));
20 
21     }
22 }
23 
24 void pSetLeft(int i)
25 {
26     if (panel_Slider.InvokeRequired)
27     {
28         panel_Slider.Invoke(new Action<int>(pSetLeft), new object[] { i });
29     }
30     else
31     {
32         panel_Slider.Left = i;
33     }
34 }
簡單實現

3,運行效果

(二)簡單實現優化

通過觀察上面的實現,可以發現雖然實現了緩入緩出效果,但是其”滑塊“(panel_Slider)的起始位置卻不是最左側,而是從中間開始。

根據上面的公式也可以看出來,當x=0時,y=(sin(x)+1)/2=1/2,即:整個擺動距離的二分之一。

(1)優化思路

為了讓滑塊從最左側開始,則需要將曲線向右移動,移動距離是π/2。

其曲線公式變為:y=(sin(x-π/2)+1)/2

如圖(藍線為 y=sin(x)):

(2)C#實現

1,佈局同上。

2,核心代碼

 1 void pShowD2()
 2 {
 3     //i是度數,不是弧度
 4     int i = 0;
 5     //移動距離要減去滑塊本身的寬度
 6     double dMoveDistance = panel_Board.Width - panel_Slider.Width;
 7     while (true)
 8     {
 9         i++;
10         if (i > 360)
11         {
12             //一個周期是360度
13             i = 0;
14         }
15         //固定時間間隔
16         Thread.Sleep(10);
17         //通過公式:弧度=度*π/180,將度數i轉為Math.Sin()所需要的弧度數
18         //因為i是度數,所以是(i-90)
19         double dz = dMoveDistance * (1 + Math.Sin((i-90) * Math.PI / 180)) / 2;
20         pSetLeft(Convert.ToInt32(dz));
21 
22     }
23 }
24 
25 void pSetLeft(int i)
26 {
27     if (panel_Slider.InvokeRequired)
28     {
29         panel_Slider.Invoke(new Action<int>(pSetLeft), new object[] { i });
30     }
31     else
32     {
33         panel_Slider.Left = i;
34     }
35 }
簡單實現(優化)

3,運行效果

(三)擴展實現

在實際應用中,動畫往往需要在”固定時間“內完成。

以前面實現為例,使滑塊從左端滑到右端的時長固定為1秒。該怎麼實現呢?

 (1)實現思路

整體思路與之前的並沒有什麼大的區別,仍是固定”時間“,變化”距離“。

在前面的”簡單實現(優化)“的基礎上,需要增加下麵的一些修改和補充:

1,假設”時間間隔“為:Interval,那麼,在指定的1秒內,共會變化(Step=1/Interval)次。

2,那麼,每次變化時度數的變化便不再是”1“,又一個周期是滑塊一個來回,所以,第次變化的度數便是:Per=2π/2*Step=180/Step。

(2)C#實現

在實現時加入了勻速的對比。

1,佈局及屬性

2,核心代碼

 1 void pShowD2()
 2 {
 3     //當前滑塊的位置
 4     double d = 0;
 5     //true/false:向右滑/向左滑
 6     bool bToRight = true;
 7     //時間間隔,單位:ms
 8     int iInterval = 10;
 9     //從左到右所需要的總時間,單位:ms
10     int iAnimateTime = 1000;
11     //移動距離要減去滑塊本身的寬度
12     double dMoveDistance = panel_Board.Width - panel_Slider.Width;
13     //需要變化的次數
14     double dStep = Convert.ToDouble(iAnimateTime) / iInterval;
15     //每次變化所增加的距離
16     double dPerX = dMoveDistance / dStep;
17     while (true)
18     {
19         d = bToRight ? d + dPerX : d - dPerX;
20         if (d > dMoveDistance)
21         {
22             bToRight = false;
23         }
24         if (d < 0)
25         {
26             bToRight = true;
27         }
28 
29         Thread.Sleep(iInterval);
30         int iZ = Convert.ToInt32(d);
31         pSetLeft2(iZ);
32 
33     }
34 }
35 void pSetLeft2(int i)
36 {
37     if (panel_S2.InvokeRequired)
38     {
39         panel_S2.Invoke(new Action<int>(pSetLeft2), new object[] { i });
40     }
41     else
42     {
43         panel_S2.Left = i;
44     }
45 }
1,勻速
 1 void pShowD()
 2 {
 3     //d是度數,不是弧度
 4     double d = 0;
 5     //時間間隔,單位:ms
 6     int iInterval = 10;
 7     //從左到右所需要的總時間,單位:ms
 8     int iAnimateTime = 1000;
 9     //移動距離要減去滑塊本身的寬度
10     double dMoveDistance = panel_Board.Width - panel_Slider.Width;
11     //需要變化的次數
12     double dStep = Convert.ToDouble(iAnimateTime) / iInterval;
13     //每次變化所增加的度數
14     double dPer = 180.0 / dStep;
15     while (true)
16     {
17         d += dPer;
18         if (d > 360)
19         {
20             //一個周期是360度
21             d = 0;
22         }
23         //固定時間間隔
24         Thread.Sleep(iInterval);
25         //通過公式:弧度=度*π/180,將度數i轉為Math.Sin()所需要的弧度數
26         double dz = dMoveDistance * (1 + Math.Sin((d - 90) * Math.PI / 180)) / 2;
27         pSetLeft(Convert.ToInt32(dz));
28 
29     }
30 }
31 
32 void pSetLeft(int i)
33 {
34     if (panel_Slider.InvokeRequired)
35     {
36         panel_Slider.Invoke(new Action<int>(pSetLeft), new object[] { i });
37     }
38     else
39     {
40         panel_Slider.Left = i;
41     }
42 }
2,緩入緩出

3,運行效果

 


 

四、結束語

本篇主要講的是三角函數與緩入緩出動畫,但三角函數在動畫中的作用不僅僅如此,比如直接使用正弦曲線的形狀來繪製波浪效果——在充電、進度條等地方可以使用該效果。

而且即然知道在曲線在動畫中的作用,那麼便可以通過不同的函數曲線實現不同的動畫效果,比如另一個非常好用的”貝塞爾曲線“,可以實現更複雜、更優雅的動畫效果。

 

如有錯誤和不足之處歡迎大家批評指正。

 


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

-Advertisement-
Play Games
更多相關文章
  • 1、將列表中的每個元素通過"_"鏈接起來 users = ['劉少奇', '李啟航', '渣渣輝']_ users = ['劉少奇', '李啟航', '渣渣輝'] s = "_".join(users) print(s) #劉少奇_李啟航_渣渣輝 2、將列表中的每個元素通過"_"鏈接起來 users ...
  • 最近有個小伙伴在群里問美團數據怎麼獲取,而且她只要火鍋數據,她在上海,只要求抓上海美團火鍋的數據,而且要求也不高,只要100條,想做個簡單的分析,相關的欄位如下圖所示。 乍一看,這個問題還真的是蠻難的,畢竟美團也不是那麼好抓,什麼驗證碼,模擬登陸等一大堆拂面而來,嚇得小伙伴都倒地了。 通過F12查看 ...
  • 你好,我是Dotnet9,繼上篇介紹Bootstrap風格的BlazorUI組件庫後,今天我來介紹另一款Blazor UI組件庫:一套基於 Ant Design 和 Blazor 的企業級組件庫。 本文導航: 一、關於Ant Design Blazor 二、Ant Design Blazor的社區貢 ...
  • nuget 包如何還原,如何修改預設的 nuget 包保存位置 ...
  • 三次握手 所謂的“三次握手”:為了對每次發送的數據量進行跟蹤與協商,確保數據段的發送和接收同步, 根據所接收到的數據量而確認數據發送、接收完畢後何時撤消聯繫,並建立虛連接。 為了提供可靠的傳送,TCP在發送新的數據之前,以特定的順序將數據包的序號,並需要這些包傳送給目標機之後的確認消息。 TCP總是 ...
  • 1 string postData = "appKey=" + appKey;//參數 2 postData += ("&appSecret=" + appSecret); 3 4 var request = (HttpWebRequest)WebRequest.Create(url); 5 req ...
  • 本文通過 WPF 的數據觸發器 (DataTrigger) 和多重綁定 (MultiBinding),在一組普通按鈕 (Button) 上實現了像單選按鈕 (RadioButton) 那樣的,同一時間只有一個按鈕具有當前樣式(本文演示的是背景顏色)的效果。 ...
  • 在源代碼中設置斷點,然後進行點擊調試 若要啟用反彙編視窗,請在工具>選項(或工具> 選項>調試下,選擇啟用地址級調試。 若要在調試期間打開反彙編視窗,請選擇視窗>反彙編或按 Alt+8 。 除彙編指令外,反彙編視窗還可顯示下列可選信息: 每條指令所在的記憶體地址 對於本機應用程式,它是實際記憶體地址。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...