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.mzph.cn/web/66778.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

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;用户…

客户服务创新:数字化时代的策略与实践

在数字化时代背景下&#xff0c;客户服务已成为企业竞争的关键领域。随着消费者需求的日益多样化和个性化&#xff0c;传统的客户服务模式已难以满足市场的要求。因此&#xff0c;企业需要不断探索和创新客户服务策略&#xff0c;以适应数字化时代的变化。 一、数字化时代客户服…

十三、数据的的输入与输出(3)

数据的输出 writeClipboard&#xff08;&#xff09;函数 writeClipboard&#xff08;&#xff09;函数可以将数据输出至剪贴板。 例如&#xff0c;将R的内置数据集iris输出到剪贴板&#xff0c;在进入Excel中点击"粘贴"。 head(iris) #查看数据集Sepal.L…

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…

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 &…

微信小程序使用picker根据接口给的省市区的数据实现省市区三级联动或者省市区街道等多级联动

接口数据如上图 省市区多级联动&#xff0c;都是使用的一个接口通过传参父类的code。返回我们想要的数据 比如获取省就直接不要参数。市就把省得code传给接口&#xff0c;区就把市的code作为参数。 <picker mode"multiSelector" :range"mulSelect1" …

VOSK实现【离线中文语音】识别

Vosk是一款开源的离线语音识别工具包&#xff0c;具有以下功能&#xff1a; 多语言支持&#xff1a;能够对20多种语言和方言进行语音识别&#xff0c;如中文、英语、德语、法语、西班牙语等&#xff0c;可满足不同用户的语言需求。 模型轻量化&#xff1a;每种语言的模型大小仅…

【Maui】注销用户,采用“手势”点击label弹窗选择

文章目录 前言一、问题描述二、解决方案三、软件开发&#xff08;源码&#xff09;3.1 方法一&#xff1a;前端绑定3.2 方法二&#xff1a;后端绑定3.3 注销用户的方法 四、项目展示 前言 .NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架&#xff0c;用于使用 C# 和 XAML 创…

macOS使用LLVM官方发布的tar.xz来安装Clang编译器

之前笔者写过一篇博文ubuntu使用LLVM官方发布的tar.xz来安装Clang编译器介绍了Ubuntu下使用官方发布的tar.xz包来安装Clang编译。官方发布的版本中也有MacOS版本的tar.xz&#xff0c;那MacOS应该也是可以安装的。 笔者2015款MBP笔记本&#xff0c;CPU是intel的&#xff0c;出厂…

中企出海:从国际投资建厂:投前投中投后重点事项

1. 投前重点事项 1.1 市场调研与分析 在国际投资建厂的投前阶段&#xff0c;市场调研与分析是至关重要的基础工作&#xff0c;它能够帮助企业全面了解目标市场&#xff0c;为后续决策提供有力依据。 市场规模与潜力&#xff1a;通过收集和分析目标国家或地区的经济数据、行业…

Git实用指南:忽略文件、命令别名、版本控制、撤销修改与标签管理

目录 1.忽略特殊文件 1.1.那如何配置我们需要忽略的文件的呢&#xff1f; 1.2.如何检验效果&#xff1f; 2.给命令配置别名 3.基本操作之版本回退 3.1.使用场景&#xff1a; 3.2.使用方法&#xff1a; 4.撤销修改 情况一&#xff1a;对于工作区的代码&#xff0c;还没…

Glary Utilities Pro 多语便携版系统优化工具 v6.21.0.25

Glary Utilities是一款功能强大的系统优化工具软件&#xff0c;旨在帮助用户清理计算机垃圾文件、修复系统错误、优化系统性能等。 软件功能 清理和修复&#xff1a;可以清理系统垃圾文件、无效注册表项、无效快捷方式等&#xff0c;修复系统错误和蓝屏问题。 优化和加速&…

Oracle 创建并使用外部表

目录 一. 什么是外部表二. 创建外部表所在的文件夹对象三. 授予访问外部表文件夹的权限3.1 DBA用户授予普通用户访问外部表文件夹的权限3.2 授予Win10上的Oracle用户访问桌面文件夹的权限 四. 普通用户创建外部表五. 查询六. 删除 一. 什么是外部表 在 Oracle 数据库中&#x…