Qt實踐:一個簡單的絲滑側滑欄實現

Qt實踐:一個簡單的絲滑側滑欄實現

筆者前段時間突然看到了側滑欄,覺得這個抽屜式的側滑欄非常的有趣,打算這里首先嘗試實現一個簡單的絲滑側滑欄。

首先是上效果圖

(C,GIF幀率砍到毛都不剩了)

QPropertyAnimation

官方的QPropertyAnimation Class | Qt Core 6.8.1

也就是說,這個類封裝了我們的Qt動畫播放的類,我們針對Widgets的屬性對其變化進行動畫播放。Qt的抽象非常的好,只需要設置我們的起點和終點狀態,以及設置一下時間間隔和播放的變化方式,就完事了。

  • setDuration(int msec): 設置動畫的持續時間,單位是毫秒。

  • setStartValue(const QVariant &startValue): 設置動畫的起始值。

  • setEndValue(const QVariant &endValue): 設置動畫的結束值。

  • setEasingCurve(const QEasingCurve &curve): 設置動畫的插值曲線,控制動畫的速度變化(如加速、減速、勻速等)。常用的曲線類型有 QEasingCurve::LinearQEasingCurve::InQuadQEasingCurve::OutBounce 等。

筆者實現的效果的API就是用到了上面四個。

首先我們思考一下,SideBar看似是一個側滑欄,但是跟隨變動的,考慮上夾在中間的按鈕,是三個部分。我們按照上面的思考思路。

1. 隱藏側邊欄(do_hide_animations

使側邊欄從可見狀態過渡到隱藏狀態。具體變化如下:

  • 側邊欄動畫 (animation_side)

    • 起始狀態:側邊欄的當前幾何位置(ui->widgetSiderBar->geometry())。

    • 結束狀態:側邊欄移動到視圖外部,即其橫坐標變為負值,具體位置為 ( - ui->widgetSiderBar->width(), ui->widgetSiderBar->y() )。這樣側邊欄就被“隱藏”到屏幕外。

  • 按鈕動畫 (animation_button)

    • 起始狀態:操作按鈕當前的幾何位置(ui->btn_operate->geometry())。

    • 結束狀態:按鈕的位置將移動到屏幕左側,具體位置為 ( 0, ui->btn_operate->y() )。這樣按鈕會被移到左側,表示側邊欄已隱藏。

  • 主界面動畫 (animation_main)

    • 起始狀態:主界面的當前幾何位置(ui->widget_mainside->geometry())。

    • 結束狀態:主界面位置根據按鈕的位置進行調整,具體為 ( ui->btn_operate->width(), ui->widget_mainside->y() ),這意味著主界面會向左移動,避開被隱藏的側邊欄。

  • 操作按鈕文本

    • 操作按鈕的文本更改為 ">",表示點擊后側邊欄會“展開”。

  • 執行動畫:調用 group->start() 啟動所有動畫,產生隱藏效果。

2. 顯示側邊欄(do_show_animations

當用戶點擊按鈕以顯示側邊欄時,執行 do_show_animations,將側邊欄從隱藏狀態恢復到可見狀態。具體變化如下:

  • 側邊欄動畫 (animation_side)

    • 起始狀態:側邊欄當前的幾何位置(ui->widgetSiderBar->geometry())。

    • 結束狀態:側邊欄移動到其原始位置,即橫坐標變為 0,具體位置為 ( 0, ui->widgetSiderBar->y() ),使其重新顯示在屏幕上。

  • 按鈕動畫 (animation_button)

    • 起始狀態:操作按鈕當前的幾何位置(ui->btn_operate->geometry())。

    • 結束狀態:按鈕的位置將移動到側邊欄的右側,具體為 ( ui->widgetSiderBar->width(), ui->btn_operate->y() ),表示按鈕回到右側,側邊欄已重新顯示。

  • 主界面動畫 (animation_main)

    • 起始狀態:主界面的當前幾何位置(ui->widget_mainside->geometry())。

    • 結束狀態:主界面的位置調整為 ( ui->widgetSiderBar->width() + ui->btn_operate->width(), ui->widget_mainside->y() ),并且寬度變為 width() - ui->btn_operate->width() - ui->widgetSiderBar->width(),使得主界面重新適應顯示的側邊欄。

  • 操作按鈕文本

    • 操作按鈕的文本更改為 "<",表示點擊后側邊欄會“隱藏”。

  • 執行動畫:調用 group->start() 啟動所有動畫,產生顯示效果。

代碼上的體現就是

void SideBarWidget::do_hide_animations() {animation_side->setStartValue(ui->widgetSiderBar->geometry());/* move to the hidden place */animation_side->setEndValue(QRect(-ui->widgetSiderBar->width(), ui->widgetSiderBar->y(),ui->widgetSiderBar->width(), ui->widgetSiderBar->height()));
?animation_button->setStartValue(ui->btn_operate->geometry());animation_button->setEndValue(QRect(0, ui->btn_operate->y(),ui->btn_operate->width(),ui->btn_operate->height()));
?animation_main->setStartValue(ui->widget_mainside->geometry());animation_main->setEndValue(QRect(ui->btn_operate->width(), ui->widget_mainside->y(),width() - ui->btn_operate->width(), ui->widget_mainside->height()));
?ui->btn_operate->setText(">");group->start();
}
void SideBarWidget::do_show_animations() {animation_side->setStartValue(ui->widgetSiderBar->geometry());/* move to the hidden place */animation_side->setEndValue(QRect(0, ui->widgetSiderBar->y(),ui->widgetSiderBar->width(),ui->widgetSiderBar->height()));
?animation_button->setStartValue(ui->btn_operate->geometry());animation_button->setEndValue(QRect(ui->widgetSiderBar->width(), ui->btn_operate->y(),ui->btn_operate->width(), ui->btn_operate->height()));
?animation_main->setStartValue(ui->widget_mainside->geometry());animation_main->setEndValue(QRect(ui->widgetSiderBar->width() + ui->btn_operate->width(),ui->widget_mainside->y(),width() - ui->btn_operate->width() - ui->widgetSiderBar->width(),ui->widget_mainside->height()));ui->btn_operate->setText("<");ui->widgetSiderBar->setVisible(true);group->start();
}
上面體現了一個優化,那就是使用動畫組Group來同步的進行操作。防止出現動畫搶跑。

源碼

完整的測試源碼在:CCQt_Libs/Widget/SideBarWidget at main · Charliechen114514/CCQt_Libs (github.com)

C++源碼如下

#include "SideBarWidget.h"
#include "ui_SideBarWidget.h"
#include <QParallelAnimationGroup>
#include <QPropertyAnimation>namespace SideBarUtilsTools {
void clearLayout(QLayout* layout) {if (!layout) return;QLayoutItem* item;while ((item = layout->takeAt(0)) != nullptr) {if (item->widget()) {item->widget()->hide();  // 隱藏控件,但不刪除} else {clearLayout(item->layout());  // 遞歸清理子布局}}
}
}  // namespace SideBarUtilsToolsSideBarWidget::SideBarWidget(QWidget* parent): QWidget(parent), ui(new Ui::SideBarWidget) {ui->setupUi(this);__initMemory();__initConnection();
}void SideBarWidget::switch_state() {setState(!hidden_state);
}void SideBarWidget::switch_button_visible() {setButtonVisible(!ui->btn_operate->isVisible());
}void SideBarWidget::removeLayout(Role r) {switch (r) {case Role::SideBar:SideBarUtilsTools::clearLayout(ui->widgetSiderBar->layout());break;case Role::MainSide:SideBarUtilsTools::clearLayout(ui->widget_mainside->layout());break;}
}void SideBarWidget::setButtonVisible(bool visible) {ui->btn_operate->setVisible(visible);ui->btn_operate->setText(hidden_state ? ">" : "<");
}void SideBarWidget::addLayout(QLayout* layout, const QWidgetList& widgetList,Role r) {switch (r) {case Role::SideBar:ui->widgetSiderBar->setLayout(layout);for (auto& w : widgetList) {ui->widgetSiderBar->layout()->addWidget(w);}break;case Role::MainSide:ui->widget_mainside->setLayout(layout);for (auto& w : widgetList) {ui->widget_mainside->layout()->addWidget(w);}break;}
}/* setTypes */
void SideBarWidget::setAnimationDuration(int duration) {animation_button->setDuration(duration);animation_main->setDuration(duration);animation_side->setDuration(duration);
}
void SideBarWidget::setAnimationCurve(QEasingCurve::Type curve) {animation_button->setEasingCurve(curve);animation_main->setEasingCurve(curve);animation_side->setEasingCurve(curve);
}void SideBarWidget::__initMemory() {animation_main = new QPropertyAnimation(ui->widget_mainside, "geometry");animation_main->setDuration(SideBarWidgetStaticConfig::ANIMATION_DURATION);animation_main->setEasingCurve(SideBarWidgetStaticConfig::ANIMATION_CURVE);animation_side = new QPropertyAnimation(ui->widgetSiderBar, "geometry");animation_side->setDuration(SideBarWidgetStaticConfig::ANIMATION_DURATION);animation_side->setEasingCurve(SideBarWidgetStaticConfig::ANIMATION_CURVE);animation_button = new QPropertyAnimation(ui->btn_operate, "geometry");animation_button->setDuration(SideBarWidgetStaticConfig::ANIMATION_DURATION);animation_main->setDuration(SideBarWidgetStaticConfig::ANIMATION_DURATION);group = new QParallelAnimationGroup(this);group->addAnimation(animation_main);group->addAnimation(animation_side);group->addAnimation(animation_button);
}void SideBarWidget::__initConnection() {connect(ui->btn_operate, &QPushButton::clicked, this,[this]() { setState(!hidden_state); });connect(group, &QParallelAnimationGroup::finished, this, [this] {ui->widgetSiderBar->setVisible(!hidden_state);// have no better idea :(, to update the layoutresize(size().width() + 1, size().height() + 1);resize(size().width() - 1, size().height() - 1);});
}void SideBarWidget::do_hide_animations() {animation_side->setStartValue(ui->widgetSiderBar->geometry());/* move to the hidden place */animation_side->setEndValue(QRect(-ui->widgetSiderBar->width(), ui->widgetSiderBar->y(),ui->widgetSiderBar->width(), ui->widgetSiderBar->height()));animation_button->setStartValue(ui->btn_operate->geometry());animation_button->setEndValue(QRect(0, ui->btn_operate->y(),ui->btn_operate->width(),ui->btn_operate->height()));animation_main->setStartValue(ui->widget_mainside->geometry());animation_main->setEndValue(QRect(ui->btn_operate->width(), ui->widget_mainside->y(),width() - ui->btn_operate->width(), ui->widget_mainside->height()));ui->btn_operate->setText(">");group->start();
}
void SideBarWidget::do_show_animations() {animation_side->setStartValue(ui->widgetSiderBar->geometry());/* move to the hidden place */animation_side->setEndValue(QRect(0, ui->widgetSiderBar->y(),ui->widgetSiderBar->width(),ui->widgetSiderBar->height()));animation_button->setStartValue(ui->btn_operate->geometry());animation_button->setEndValue(QRect(ui->widgetSiderBar->width(), ui->btn_operate->y(),ui->btn_operate->width(), ui->btn_operate->height()));animation_main->setStartValue(ui->widget_mainside->geometry());animation_main->setEndValue(QRect(ui->widgetSiderBar->width() + ui->btn_operate->width(),ui->widget_mainside->y(),width() - ui->btn_operate->width() - ui->widgetSiderBar->width(),ui->widget_mainside->height()));ui->btn_operate->setText("<");ui->widgetSiderBar->setVisible(true);group->start();
}SideBarWidget::~SideBarWidget() {delete ui;
}

接口文件如下:

#ifndef SIDEBARWIDGET_H
#define SIDEBARWIDGET_H#include <QEasingCurve>
#include <QWidget>
class QPropertyAnimation;
class QParallelAnimationGroup;
namespace SideBarWidgetStaticConfig {
static constexpr const bool               INIT_STATE         = false;
static constexpr const int                ANIMATION_DURATION = 500;
static constexpr const QEasingCurve::Type ANIMATION_CURVE =QEasingCurve::InOutQuad;
};  // namespace SideBarWidgetStaticConfignamespace Ui {
class SideBarWidget;
}class SideBarWidget : public QWidget {Q_OBJECTpublic:explicit SideBarWidget(QWidget* parent = nullptr);void inline showSideBar() {setState(false);}void inline hideSideBar() {setState(true);}enum class Role { SideBar, MainSide };/* addWidgets to the two sides */void addLayout(QLayout* layout, const QWidgetList& widgetList, Role r);/* remove the display widgets */void removeLayout(Role r);/* enable or disable the button visibilities */void setButtonVisible(bool visible);/* setTypes and durations */void setAnimationDuration(int duration);void setAnimationCurve(QEasingCurve::Type curve);~SideBarWidget();
public slots:void switch_state();void switch_button_visible();private:QPropertyAnimation*      animation_main;QPropertyAnimation*      animation_side;QPropertyAnimation*      animation_button;QParallelAnimationGroup* group;void inline setState(bool st) {hidden_state = st;hidden_state ? do_hide_animations() : do_show_animations();}void               __initMemory();void               __initConnection();void               do_hide_animations();void               do_show_animations();bool               hidden_state{SideBarWidgetStaticConfig::INIT_STATE};Ui::SideBarWidget* ui;
};#endif  // SIDEBARWIDGET_H

Reference

感謝https://zhuanlan.zhihu.com/p/614475116?utm_id=0,我的設計幾乎從這里派生出來!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/66778.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/66778.shtml
英文地址,請注明出處:http://en.pswp.cn/web/66778.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

工作流引擎Camunda與LiteFlow核心組件對比

以下為 Camunda 7 和 LiteFlow 詳細的介紹&#xff0c;包括它們的核心組件和用途。 1. Camunda 7 詳細介紹 Camunda 7 是一個基于 BPMN 2.0 標準的企業級工作流和決策自動化平臺。它被廣泛應用于復雜業務流程的管理和執行&#xff0c;其核心目標是通過流程自動化來提升企業效…

css動畫水球圖

由于echarts水球圖動畫會導致ios卡頓&#xff0c;所以純css模擬 展示效果 組件 <template><div class"water-box"><div class"water"><div class"progress" :style"{ --newProgress: newProgress % }"><…

iOS 權限管理:同時請求相機和麥克風權限的最佳實踐

引言 在開發視頻類應用時&#xff0c;我們常常會遇到需要同時請求相機和麥克風權限的場景。比如&#xff0c;在用戶發布視頻動態時&#xff0c;相機用于捕捉畫面&#xff0c;麥克風用于錄制聲音&#xff1b;又或者在直播功能中&#xff0c;只有獲得這兩項權限&#xff0c;用戶…

Java 泛型上下限詳解:以 Info 泛型類和方法實現為例

本文將通過一個實際示例&#xff0c;來深入講解 Java 泛型中的上下限及其應用場景。在這個示例中&#xff0c;我們會實現一個泛型類 Info 和兩個泛型方法 upperLimit 和 lowerLimit&#xff0c;并解釋其工作機制。 1. 什么是 Java 泛型上下限&#xff1f; Java 泛型的上下限是…

客戶服務創新:數字化時代的策略與實踐

在數字化時代背景下&#xff0c;客戶服務已成為企業競爭的關鍵領域。隨著消費者需求的日益多樣化和個性化&#xff0c;傳統的客戶服務模式已難以滿足市場的要求。因此&#xff0c;企業需要不斷探索和創新客戶服務策略&#xff0c;以適應數字化時代的變化。 一、數字化時代客戶服…

【PyCharm】遠程連接Linux服務器

【PyCharm】相關鏈接 【PyCharm】連接Jupyter Notebook【PyCharm】快捷鍵使用【PyCharm】遠程連接Linux服務器【PyCharm】設置為中文界面 【PyCharm】遠程連接Linux服務器 PyCharm 提供了遠程開發的功能&#xff0c;使得開發者可以在本地編輯代碼或使用服務器資源。 下面將詳…

十三、數據的的輸入與輸出(3)

數據的輸出 writeClipboard&#xff08;&#xff09;函數 writeClipboard&#xff08;&#xff09;函數可以將數據輸出至剪貼板。 例如&#xff0c;將R的內置數據集iris輸出到剪貼板&#xff0c;在進入Excel中點擊"粘貼"。 head(iris) #查看數據集Sepal.L…

PyQt5之QDialog

1.描述 QDialog是對話窗口的基類&#xff0c;對話窗口是頂級窗口&#xff0c;主要用于短期任務和與用戶的簡短通信。 可分為模態對話框和非模態對話框。 模態對話框又可以分為應用程序級別和窗口級別。 ? 應用程序級別&#xff1a;當該種模態的對話框出現時&#xff0c;用…

Next.js:構建大模型智能體GPT研究者應用的 Web開發框架

Next.js&#xff1a;構建大模型智能體GPT研究者應用的 Web開發框架 Next.js 基礎知識 Next.js 是由 Vercel 公司開發維護的框架&#xff0c;極大地簡化了 React 應用的開發流程。其核心特性包括&#xff1a; 服務器端渲染&#xff08;SSR&#xff09;與靜態站點生成&#xff…

車載軟件架構 --- CP和AP作為中央計算平臺的軟件架構雙核心

我是穿拖鞋的漢子&#xff0c;魔都中堅持長期主義的汽車電子工程師。 老規矩&#xff0c;分享一段喜歡的文字&#xff0c;避免自己成為高知識低文化的工程師&#xff1a; 簡單&#xff0c;單純&#xff0c;喜歡獨處&#xff0c;獨來獨往&#xff0c;不易合同頻過著接地氣的生活…

華為EC6110T-海思Hi3798MV310_安卓9.0_通刷-強刷固件包

華為EC6110T-海思Hi3798MV310_安卓9.0_通刷-強刷固件包 刷機教程說明&#xff1a; 適用機型&#xff1a;華為EC6110-T、華為EC6110-U、華為EC6110-M 破解總分為兩個部分&#xff1a;拆機短接破解&#xff08;保留IPTV&#xff09;和OTT卡刷&#xff08;不保留IPTV&#xff09…

Element使用表單重置如果不使用prop,重置無法生效

文章目錄 為什么需要 prop&#xff1f;示例&#xff1a;使用 prop 的正確方式關鍵點總結 在 element-ui 的 el-form 組件中&#xff0c; prop 屬性是與表單驗證和表單字段綁定密切相關的&#xff0c;尤其在使用 resetFields() 重置表單數據時。 如果不使用 prop&#xff0…

使用pyboard、micropython和tja1050進行can通信

單片機和can收發器之間tx、rx不需要交叉接線&#xff01;&#xff01;&#xff01; tja1050的rx接Y3、tx接Y4 from pyb import CANcan CAN(1) can.init(modecan.NORMAL, prescaler6, sjw1, bs14, bs22, auto_restartTrue) # 1Mbps的配置&#xff0c;本文使用的micropython1.…

【信息系統項目管理師】高分論文:論信息系統項目的干系人管理(社保信息管理系統)

更多內容請見: 備考信息系統項目管理師-專欄介紹和目錄 文章目錄 論文1、識別干系人2、規劃干系人參與3、管理干系人4、監督干系人論文 2016年3月,我作為項目經理參與了XX市社保信息管理系統項目的建設,該項目投資共450萬元人民幣,建設工期為1年,通過該項目的實施,在XX市…

JavaScript系列(39)-- Web Workers技術詳解

JavaScript Web Workers技術詳解 &#x1f504; 今天&#xff0c;讓我們深入了解Web Workers技術&#xff0c;這是一種能夠在后臺線程中運行腳本的強大特性&#xff0c;可以避免阻塞主線程&#xff0c;提升Web應用的性能和響應性。 Web Workers基礎概念 &#x1f31f; &#…

26、正則表達式

目錄 一. 匹配字符 .&#xff1a;匹配除換行符外的任意單個字符。 二. 位置錨點 ^&#xff1a;匹配輸入字符串的開始位置。 $&#xff1a;匹配輸入字符串的結束位置。 \b&#xff1a;匹配單詞邊界。 \B&#xff1a;匹配非單詞邊界。 三. 重復限定符 *&#xff1a;匹配…

Chrome遠程桌面無法連接怎么解決?

Chrome遠程桌面連接已停止工作 Chrome遠程桌面是一款極為便捷的瀏覽器插件&#xff0c;能夠幫助用戶將自己的計算機連接到其他設備&#xff0c;無論是手機、平板電腦還是其他電腦。然而&#xff0c;在實際使用中&#xff0c;許多用戶可能會面臨各種各樣的問題&#xff0c;比如…

備賽藍橋杯之第十五屆職業院校組省賽第一題:智能停車系統

提示&#xff1a;本篇文章僅僅是作者自己目前在備賽藍橋杯中&#xff0c;自己學習與刷題的學習筆記&#xff0c;寫的不好&#xff0c;歡迎大家批評與建議 由于個別題目代碼量與題目量偏大&#xff0c;請大家自己去藍橋杯官網【連接高校和企業 - 藍橋云課】去尋找原題&#xff0…

基于AutoDL云計算平臺+LLaMA-Factory訓練平臺微調本地大模型

1. 注冊與認證 訪問AutoDL官網&#xff1a;前往 AutoDL官網。 注冊賬號&#xff1a;完成注冊流程。 實名認證&#xff1a;按照要求完成實名認證&#xff0c;以確保賬號的合規性。 2. 選擇GPU資源 進入算力市場&#xff1a;在官網首頁點擊“算力市場”菜單。 挑選GPU&#x…

C語言練習(19)

已知5個學生的4門課的成績&#xff0c;要求求出每個學生的平均成績&#xff0c;然后對平均成績從高到低將各學生的成績記錄排序&#xff08;成績最高的學生排在數組最前面的行&#xff0c;成績最低的學生排在數組最后面的行&#xff09;。 #include <stdio.h> #include &…