LVGL庫入門教程 - 動畫

来源:https://www.cnblogs.com/frozencandles/archive/2022/06/29/16422019.html
-Advertisement-
Play Games

使用磁碟空間過程 分區:毛坯房 格式化:配置創建文件系統(裝修) 掛載:把分區和文件系統進行組裝 Linux一切皆文件:通過文件的方式來管理磁碟 linux一切皆文件,所以磁碟也表現為文件。每個硬碟文件命名方式和磁碟的類型有關。硬碟文件在內核中自動生成識別,並存放在/dev這個文件下麵。 lvm:邏 ...


動畫可以說是 LVGL 中的特色之一,不過在使用動畫前,請確保單片機具有足夠的性能來維持足夠的幀率。

transition:過渡動畫

當一個控制項的狀態發生改變時,可以讓樣式也發生變化以提醒用戶。通過過渡動畫(transition)可以讓樣式的改變更自然。例如,按鈕在點擊時,以及開關在切換時,都具有一小段的過渡動畫。

過渡動畫使用 lv_style_transition_dsc_t 結構描述。為了要設置過渡動畫,需要提供以下信息:

  • 哪些屬性需要過渡
  • 過渡前的延時
  • 過渡持續的時間
  • 過渡動畫(以回調函數的形式提供)

這些信息和結構成員是一一對應的。除了直接給結構成員賦值外,也可以使用以下初始化函數一次性設置:

void lv_style_transition_dsc_init(
                lv_style_transition_dsc_t* tr, 
                const lv_style_prop_t props[],
                lv_anim_path_cb_t path_cb, 
                uint32_t time, 
                uint32_t delay, 
                void* user_data);

第一個參數需要提供被初始化的過渡動畫結構,第二個參數數組和字元串一樣需要以 0 結尾。例如,假設需要實現這樣一個過渡效果:點擊時背景顏色發生改變並拉長,那麼相應的初始化過程為:

static lv_style_transition_dsc_t trans;
static const lv_style_prop_t trans_props[] = {
    LV_STYLE_WIDTH, LV_STYLE_HEIGHT, LV_STYLE_BG_COLOR, 0,
};
lv_style_transition_dsc_init(&trans, trans_props, 
            lv_anim_path_ease_in_out, 500, 0, NULL);

這裡使用的過渡函數為 lv_anim_path_ease_in_out() ,這是一個內置的過渡效果,與之類似的過渡lv_anim_path_ease_out函數可以參考下表:

過渡函數 過渡效果
lv_anim_path_linear 等速過渡
lv_anim_path_ease_in 先慢後快的過渡
lv_anim_path_ease_out 先快後慢的過渡
lv_anim_path_ease_in_out 先慢、後快、結尾再變慢的過渡
lv_anim_path_overshoot 幅度會稍微過頭一些再彈回的過渡
lv_anim_path_bounce 和上一個類似,不過會比較快地多彈幾次
lv_anim_path_step 一步到位,和沒動畫的區別在於多了個延時

過渡動畫是控制項樣式的一部分,可以將初始化得到的過渡動畫描述應用到樣式上:

static lv_style_t style_trans;
lv_style_init(&style_trans);
lv_style_set_transition(&style_trans, &trans);

過渡動畫只有在兩種樣式切換時才會發生。例如,如果讓以上樣式應用在按下狀態下:

lv_style_set_bg_color(&style_trans, lv_palette_main(LV_PALETTE_RED));
lv_style_set_width(&style_trans, 150);
lv_style_set_height(&style_trans, 60);
lv_obj_add_style(obj, &style_trans, LV_STATE_PRESSED);

那麼只有在從其它狀態變為按下時才會發生過渡:

註意鬆開時樣式是突然轉變的。如果要給這部分也添加一個過渡效果,可以給預設狀態下的控制項添加一個包含過渡的樣式。

animate:通用動畫

過渡只有在狀態改變時才會發生,而動畫可以在任意時刻進行。除此之外,兩者的區別還有:過渡只是樣式的一部分,而動畫和樣式之間是獨立的。

實際上,過渡的底層也使用的是動畫。

創建動畫

為了創建動畫,需要像樣式一樣聲明一個動畫類型並初始化:

lv_anim_t anim;
lv_anim_init(&anim);

由於動畫是立即執行的,因此可以使用自動變數存儲。然後,需要明確該動畫將作用於哪一個控制項:

lv_anim_set_var(&anim, obj);

接下來,可以設置動畫的各種軌跡,包括:

  • 動畫需要改變什麼屬性
  • 這些屬性改變的範圍
  • 動畫效果
  • 延時和持續時間

動畫的這些屬性和過渡是類似的。例如,假設想做一個控制項下落的動畫,那麼需要提供一個改變 y 坐標值的回調函數,這個函數可以直接使用 lv_obj_set_y() ,然後設定改變的始末值和運動軌跡,對應的代碼為:

lv_anim_set_exec_cb(&anim, (lv_anim_exec_xcb_t)lv_obj_set_y);
lv_anim_set_values(&anim, -100, 100);
lv_anim_set_path_cb(&anim, lv_anim_path_bounce);
lv_anim_set_time(&anim, 1000);
lv_anim_set_delay(&anim, 1000);

然後,可以在必要的時候執行動畫:

lv_anim_start(&anim);

效果為:

關於延遲渲染
之前說過,樣式是延遲渲染的,因此樣式變數需要使用 static 存儲類型修飾符;而動畫不是,動畫從創建到執行是立即發生的。這也很好理解:樣式在創建的過程中可能發生多次修改,因此需要確定最終的表現結果如何,再著手繪製,否則整個控制項可能會重繪多次,占用大量無效的資源。
這種特點可能會帶來許多意想不到的問題。例如,假設在 lv_anim_set_values() 函數中去獲取一個控制項的位置、寬度等信息,由於它們都屬於樣式的一部分,此時還沒有實際計算,因此得到的可能是預設值,造成動畫始末效果偏離預期軌跡。
要解決這個問題,要麼手動設置具體的值,要麼讓動畫等到實際渲染髮生了再執行,例如將其作為事件回調函數中的一部分。

更複雜的動畫

以上創建的動畫是單次不重覆的,LVGL 提供了許多函數,可以為動畫設置更複雜的屬性。

這裡介紹一個控制項 bar ,它實質上就是沒有 knob 部分的滑塊,可以借用該控制項來創建一個進度條(progress bar)動畫。以下創建一個 bar 並將它的模式設定為 LV_BAR_MODE_RANGE ,這樣就可以同時修改 indicator 兩端的位置了:

lv_obj_t* bar = lv_bar_create(lv_scr_act());
lv_bar_set_mode(bar, LV_BAR_MODE_RANGE);

這裡使用官方文檔中提供的一個樣式來使外觀更好看,具體細節就無需解釋了:

static lv_style_t style_bg;
static lv_style_t style_indic;
lv_style_init(&style_bg);
lv_style_set_border_color(&style_bg, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_border_width(&style_bg, 2);
lv_style_set_pad_all(&style_bg, 6);
lv_style_set_radius(&style_bg, 6);
lv_style_set_anim_time(&style_bg, 1000);
lv_style_init(&style_indic);
lv_style_set_bg_opa(&style_indic, LV_OPA_COVER);
lv_style_set_bg_color(&style_indic, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_radius(&style_indic, 3);
lv_obj_remove_style_all(bar);
lv_obj_add_style(bar, &style_bg, 0);
lv_obj_add_style(bar, &style_indic, LV_PART_INDICATOR);
lv_obj_set_size(bar, 200, 20);

然後就可以確定動畫效果了。例如,這裡期望的動畫效果為:

那麼首先可以編寫一個改變屬性的回調函數,例如改變 indicator 的範圍:

static void anim_progress_load(void* obj, int32_t v) {
    lv_bar_set_start_value(obj, v, LV_ANIM_ON);
    lv_bar_set_value(obj, 20 + v, LV_ANIM_ON);
}

這些值在 0~80 範圍內等速改變,持續時間 1.5 秒,無延時,對應的代碼為:

lv_anim_set_exec_cb(&anim, anim_progress_load);
lv_anim_set_values(&anim, 0, 80);
lv_anim_set_path_cb(&anim, lv_anim_path_linear);
lv_anim_set_time(&anim, 1500);
lv_anim_set_delay(&anim, 0);

然後這裡為其添加一個倒退和重覆效果,這樣動畫就能來回播放了:

lv_anim_set_playback_time(&anim, 1500);
lv_anim_set_repeat_count(&anim, LV_ANIM_REPEAT_INFINITE);

實現的進度條動畫就像以上 gif 展示的一樣。除此之外,還可以修改更多動畫的細節,例如:

函數 設置內容
lv_anim_set_start_cb(anim, start_cb) 在延時後、開始前執行一個函數
lv_anim_set_playback_delay(anim, delay) 設置動畫倒退前的延時
lv_anim_set_repeat_delay(anim, delay) 設置動畫重覆前的延時
lv_anim_set_early_apply(&a, bool) 是否將起始值應用到動畫開始前,使動畫執行時不會太突兀

更多的細節可以參考官方文檔。

組合動畫效果

有時候需要同時播放較多動畫,此時如果逐個播放的話,需要逐個為動畫設計延時,不方便安排。此時,可以使用 LVGL 提供的時間線(timeline)統一安排各個動畫。

時間線的創建非常簡單。首先,創建一系列動畫,但先不調用 lv_anim_start() 讓動畫開始。

其次,創建一個時間線並將各個動畫添加到時間線的某一時刻處:

lv_anim_timeline_t* anim_timeline = lv_anim_timeline_create();
lv_anim_timeline_add(anim_timeline, 0, &anim_axis);
lv_anim_timeline_add(anim_timeline, 100, &anim_obj_01);
lv_anim_timeline_add(anim_timeline, 1100, &anim_obj_02);
lv_anim_timeline_add(anim_timeline, 2100, &anim_obj_03);
lv_anim_timeline_add(anim_timeline, 300, &anim_label_01);
lv_anim_timeline_add(anim_timeline, 1300, &anim_label_02);
lv_anim_timeline_add(anim_timeline, 2300, &anim_label_03);

使用時間線時,無需為動畫設計延時,只需要關註動畫會在什麼時刻播放,延時便會自動計算。

添加完畢後,再調用時間線的執行函數就可以了:

lv_anim_timeline_start(anim_timeline);

這樣就可以創建很複雜的組合動畫效果了:

使用時間線可以方便管理所有動畫,可以將時間線上包含的所有動畫停播、倒放、跳轉等。以下列出了一些常用的時間線控制函數:

函數 用途
lv_anim_timeline_stop(timeline) 暫停播放當前的所有動畫
lv_anim_timeline_set_reverse(timeline, bool) 設置接下來的播放方向
lv_anim_timeline_set_progress(timeline, progress) 跳轉到播放進度

如果需要倒放,在設置了播放方向後還需要調用 lv_anim_timeline_start() 重新播放,並且會從當前位置倒放。

scroll:滾動動畫

滾動的特點

滾動也是常見的一種動畫效果。如果一個容器的尺寸不足以容納它包含的控制項,那麼它就可以通過滾動來展示包含控制項的所有部分。

為了使一個控制項是可滾動的,它需要擁有標誌 LV_OBJ_FLAG_SCROLLABLE 。清除該標誌可以隱藏子控制項的溢出部分。

滾動是可以冒泡的,如果一個控制項已經滾動到底,再次對其嘗試滾動將使滾動事件傳播到父容器上。可以通過清除 LV_OBJ_FLAG_SCROLL_CHAIN 標誌位去除這個性質。

可以通過 lv_obj_set_scroll_dir() 限制滾動的方向。例如:

lv_obj_set_scroll_dir(obj, LV_DIR_RIGHT);

那麼就只能向右滾動到底,不能向左折回。

還可以通過以下幾個函數利用代碼執行滾動:

lv_obj_scroll_to(obj, x, y, anim_en);
lv_obj_scroll_by(obj, x, y, anim_en);
lv_obj_scroll_to_view(child, anim_en);

註意前兩個函數的區別:前者是滾動到相應的位置,多次調用只有第一次實際有效;後者是模擬滾動的操作,實際滾動方向是相反的,並且多次調用效果可以疊加。除此之外,後者甚至可以滾動到超出子控制項的範圍之外。最後一個函數自動滾動到合適的位置,確保子控制項可視。

這幾個函數都不受滾動方向的約束。它們都具有第三個參數,用於指定滾動時是否提供滾動動畫。

滾動動畫

滾動是有動畫的,預設情況下,滾動動畫的特點表現在以下幾點:

  • 滾動是具有慣性的,意思是當輸入設備停止交互時,控制項還會繼續向前滾動一小段距離。可以通過清除 LV_OBJ_FLAG_SCROLL_MOMENTUM 標誌位取消這個特征
  • 滾動是具有彈性的,當滾動到底時,繼續嘗試滾動會使控制項超出一定範圍,鬆開後回彈。可以通過清除 LV_OBJ_FLAG_SCROLL_ELASTIC 標誌位取消這個特征
  • 除此之外,以上介紹的兩個代碼實現滾動的函數,如果在第三個參數中應用滾動,那麼會發生一小段 easy-out 的切換動畫

還可以設置一種特殊的滾動效果 snap ,它使滾動時可以自動對齊。為了啟用這種效果,需要添加 LV_OBJ_FLAG_SNAPPABLE 標誌位,然後設置對齊的方式:

lv_obj_set_scroll_snap_x(cont, LV_SCROLL_SNAP_START);

這樣便可以按開始位置對齊了:

還可以配合 LV_OBJ_FLAG_SCROLL_ONE 標誌位一次只滾過最多一個控制項的位置。


在滾動時,會觸發 LV_EVENT_SCROLL 事件,可以通過在該事件回調函數中對包含的子控制項做變換,實現更複雜的滾動效果。

例如,以下在事件回調函數內,根據每個子控制項當前位置的縱坐標對橫坐標做一些變換:

static scrool_widget_cb(lv_event_t* e) {
    lv_obj_t* cont = lv_event_get_target(e);
    uint32_t child_cnt = lv_obj_get_child_cnt(cont);
    for (uint8_t i = 0; i < child_cnt; i++) {
        lv_obj_t* child = lv_obj_get_child(cont, i);
        lv_obj_set_style_translate_x(child, child->coords.y1 * 0.5 - 60, 0);
    }
}

然後讓每次滾動時都做以上變換:

lv_obj_add_event_cb(cont, scrool_widget_cb, LV_EVENT_SCROLL, NULL);

這樣就能實現斜方向的滾動效果了:

這裡由於僅在事件中才修改按鈕的水平位置,因此一開始控制項的擺放不是傾斜的。要解決這個問題,可以添加以下代碼:

lv_obj_scroll_to_view(lv_obj_get_child(cont, 0), LV_ANIM_OFF);
lv_event_send(cont, LV_EVENT_SCROLL, NULL);

前者使各個控制項的坐標被計算,後者手動觸發事件回調函數,利用計算出的坐標執行位置變換。

LVGL 的官方文檔還給出了一個示例,可以實現類似圓形的旋轉滾動,效果非常不錯,不過涉及的計算較多,感興趣的可以自行閱讀官方文檔。

滾動條

如果一個控制項可以發生滾動,那麼它就具有滾動條(scrollbar)。可以通過 lv_obj_set_scrollbar_mode() 函數修改滾動條的模式。例如,使用 LV_SCROLLBAR_MODE_OFF 模式可以使滾動條完全消失,就像上一張 gif 顯示的那樣。

滾動條是一個控制項的 LV_PART_SCROLLBAR 部分,可以通過選擇器給滾動條加上不同的樣式。

首發於:http://frozencandles.fun/archives/425

參考資料/延伸閱讀

https://docs.lvgl.io/master/overview/animation.html

https://docs.lvgl.io/master/overview/scroll.html


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

-Advertisement-
Play Games
更多相關文章
  • List 數據結構 Redis 3.2 前,使用 壓縮列表zipList 或 雙向鏈表linkedList 當同時滿足下麵兩個條件時,使用zipList存儲數據 list保存的每個元素長度小於64位元組 列表中數據個數少於512個 Redis 3.2 及之後的底層實現方式: quickList qui ...
  • 中移OneOS開發板學習入門 (做為一個專業的點燈愛好者,學習任何開發板開始前都是先找好學習資料,然後在學習OneOS的內核部分,外設部分,系統組件等) OneOS OneOS是中國移動針對物聯網領域推出的輕量級操作系統,具有可裁剪、跨平臺、低功耗、高安全等特點,支持ARM Cortex-A和 Co ...
  • 備份資料庫 問題描述: ​ 我們用的是mysql,以今天遇到的情況為例,我們是在兩台伺服器上要搭相同的平臺,部署完成後頁面報錯,發現是資料庫的問題,我們打開資料庫查看,確實資料庫中少建一個wind資料庫,但是我們沒有建這個資料庫的腳本,資料庫裡面涉及到很多表,很複雜,於是採用linux備份的方法,成 ...
  • 通過 hosts文件配置本地功能變數名稱 概念 DNS: 功能變數名稱系統(Domain Name System),是互聯網的一項服務。它作為將功能變數名稱和IP地址相互映射的一個分散式資料庫,能夠使人更方便地訪問互聯網。 將功能變數名稱映射到對應的IP地址。 互聯網通過IP定位瀏覽器建立連接,但是我們不易區別IP,為了方便用戶辨 ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 作為一個專業小白,咱啥都不懂。 linux不懂,docker不懂。 但是我還想要完成領導下達的任務:在linux中安裝docker後部署數據可視化工具。作為一名敬業 的打工人擺爛不可以,躺平不可以,弱小,無助,可憐中。。這能力,這要去,要不直接散 ...
  • 編寫MBR主引導記錄,開始掌權 一.一些說明 CPU的硬體電路被設計成只能運行處於記憶體中的程式,這是硬體基因的問題,其原因是首先記憶體比較快且容量大,其次由於各個硬體特性不同,若被設計成運行硬體里的程式則操作系統要分別考慮每種硬體特性才行,為了達到統一,故選擇只運行記憶體中的程式。其次記憶體不僅僅是DRA ...
  • 桌面右鍵之後,點擊新建,感覺出來了的內容太雜亂了,看強迫症犯了,如圖: 強迫症患者,表示不能忍 1. win + R打開註冊表 2.在註冊表上複製: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Discar ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 MariaDB的安裝與配置 (菜雞我是用阿裡源安裝的) MariaDB是MySQL的一個分支,由開源社區維護,採用GPL授權許可,完全相容MySQL. 1.安裝相對應的源 vi /etc/yum.repos.d/MariaDB.repo # 填寫 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...