当前位置: 首页 > news >正文

Qt 自定义控件动画深度解析:从 QPropertyAnimation 到源码内幕

一、Qt 动画的三层架构

Qt 的动画系统不是"一套 API 打天下",而是三层抽象,各有适用场景:

┌─────────────────────────────────────────────────────────────────────┐ │ Layer 3: QStateMachine + QState (状态机) │ │ 复杂状态转换、条件触发、历史状态、并行状态 │ │ 适用:复杂 UI 状态流转(向导、多步骤流程) │ └────────────────────────────┬────────────────────────────────────────┘ │ 内部用 QAbstractTransition + QPropertyAnimation ┌────────────────────────────▼────────────────────────────────────────┐ │ Layer 2: QAnimationGroup (动画组) │ │ QSequentialAnimationGroup / QParallelAnimationGroup │ │ 适用:复杂动画编排(串行、并行、嵌套) │ └────────────────────────────┬────────────────────────────────────────┘ │ 内部管理 QAbstractAnimation 子类 ┌────────────────────────────▼────────────────────────────────────────┐ │ Layer 1: QPropertyAnimation / QVariantAnimation (原子动画) │ │ 单属性插值、缓动曲线、时间线 │ │ 适用:单控件单属性动画(位置、透明度、颜色) │ └─────────────────────────────────────────────────────────────────────┘

选择原则:

场景推荐 API
按钮点击后渐隐QPropertyAnimation+opacity
控件从 A 移动到 BQPropertyAnimation+geometrypos
进度条从 0 到 100QPropertyAnimation+value
串行动画(先移动再缩放)QSequentialAnimationGroup
并行动画(移动 + 旋转同时)QParallelAnimationGroup
复杂状态流转(登录→主页→详情)QStateMachine+QState

二、QPropertyAnimation:属性动画核心

2.1 基本用法

// 最简单的属性动画:移动控件 QPropertyAnimation *animation = new QPropertyAnimation(button, "geometry"); animation->setDuration(1000); // 1 秒 animation->setStartValue(QRect(0, 0, 100, 30)); animation->setEndValue(QRect(200, 150, 100, 30)); animation->setEasingCurve(QEasingCurve::OutBounce); // 弹跳缓动 animation->start(QAbstractAnimation::DeleteWhenStopped);

关键点:QPropertyAnimation通过 Qt 的属性系统工作。目标对象必须有Q_PROPERTY,且该属性有READ/WRITE函数。

2.2 自定义控件动画属性

// 自定义圆形进度条 class CircularProgress : public QWidget { Q_OBJECT Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged) Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) public: explicit CircularProgress(QWidget *parent = nullptr); int value() const { return m_value; } void setValue(int v) { if (m_value == v) return; m_value = v; update(); // 触发重绘 emit valueChanged(v); } qreal angle() const { return m_angle; } void setAngle(qreal a) { m_angle = a; update(); emit angleChanged(a); } protected: void paintEvent(QPaintEvent *) override { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); // 背景圆环 painter.setPen(QPen(Qt::gray, 8)); painter.drawArc(rect().adjusted(10, 10, -10, -10), 0, 360 * 16); // 进度圆弧 painter.setPen(QPen(Qt::blue, 8)); int span = static_cast<int>(m_angle * 16); // 1/16 度单位 painter.drawArc(rect().adjusted(10, 10, -10, -10), 90 * 16, -span); // 中心文字 painter.drawText(rect(), Qt::AlignCenter, QString::number(m_value) + "%"); } signals: void valueChanged(int); void angleChanged(qreal); private: int m_value = 0; qreal m_angle = 0; }; // 使用动画 CircularProgress *progress = new CircularProgress(this); progress->resize(120, 120); // 动画:0° → 360° 旋转 QPropertyAnimation *anim = new QPropertyAnimation(progress, "angle"); anim->setDuration(2000); anim->setStartValue(0.0); anim->setEndValue(360.0); anim->setLoopCount(-1); // 无限循环 anim->start();

三、QPropertyAnimation 源码解析

3.1 继承层次

// qtbase/src/corelib/animation/qabstractanimation.h class Q_CORE_EXPORT QAbstractAnimation : public QObject { Q_OBJECT Q_PROPERTY(State state READ state NOTIFY stateChanged) Q_PROPERTY(int duration READ duration) Q_PROPERTY(int loopCount READ loopCount WRITE setLoopCount) public: enum State { Stopped, Paused, Running }; enum Direction { Forward, Backward }; virtual int duration() const = 0; // 动画总时长(毫秒) State state() const; void start(DeletionPolicy policy = KeepWhenStopped); void pause(); void resume(); void stop(); void setCurrentTime(int msecs); protected: virtual void updateCurrentTime(int currentTime) = 0; // 核心虚函数 virtual void updateState(State newState, State oldState); };

3.2 QVariantAnimation:插值引擎

// qtbase/src/corelib/animation/qvariantanimation.h class Q_CORE_EXPORT QVariantAnimation : public QAbstractAnimation { Q_OBJECT Q_PROPERTY(QVariant startValue READ startValue WRITE setStartValue) Q_PROPERTY(QVariant endValue READ endValue WRITE setEndValue) Q_PROPERTY(QEasingCurve easingCurve READ easingCurve WRITE setEasingCurve) public: QVariant startValue() const; QVariant endValue() const; QVariant currentValue() const; // 当前插值结果 void setEasingCurve(const QEasingCurve &easing); protected: // 核心:子类重写此方法处理插值结果 virtual void updateCurrentValue(const QVariant &value) = 0; // 插值函数(可注册自定义类型) typedef QVariant (*ValueInterpolator)(const QVariant &from, const QVariant &to, qreal progress); static void registerInterpolator(ValueInterpolator func, int type); };

3.3 插值算法源码

// qtbase/src/corelib/animation/qvariantanimation.cpp void QVariantAnimation::updateCurrentTime(int currentTime) { // 计算进度 [0, 1] qreal progress = qreal(currentTime) / qreal(duration()); // 应用缓动曲线 progress = m_easingCurve.valueForProgress(progress); // 插值 QVariant result = interpolate(m_startValue, m_endValue, progress); // 通知子类 updateCurrentValue(result);
http://www.jsqmd.com/news/625609/

相关文章:

  • 2026年四川成都办公玻璃隔断智能化方案深度横评:源头工厂直供与隐私保护的平衡之道 - 精选优质企业推荐榜
  • 音视频框架云原生应用
  • 2026年如何选择靠谱的6063铝型材厂家?从国耀铝业的实践看行业升级路径 - 企师傅推荐官
  • 【零信任AI服务网格架构】:基于eBPF+WebAssembly构建毫秒级策略引擎的9个关键决策点
  • 如何实现一个支持「撤回」功能的即时通讯系统?
  • 3分钟掌握JiYuTrainer:极域电子教室破解终极指南,告别课堂操作限制
  • [AI/应用/MCP] MCP Server/Tool 开发指南腋
  • 前端内存泄漏排查指南:Chrome DevTools高级用法
  • 2026年4月,联系别墅花园设计施工团队的实用办法,花园设计/规划设计/民宿规划设计,花园设计施工团队怎么联系 - 品牌推荐师
  • Finalshell连不上Linux?别慌!手把手教你排查并修复Connection timed out
  • 模型即服务(MaaS)架构已过时?SITS2026 2026新版标准强制要求的3类实时反馈闭环设计
  • 大模型如何在200ms内完成端侧推理?SITS2026权威披露4项轻量化部署硬核指标
  • 卡希诺水溶肥怎么样好用吗?深度实测与农户口碑
  • Python 网络编程从入门到精通:TCP/UDP/Socket 实战详解
  • Steam成就管理器完全指南:如何安全修复游戏成就问题
  • Ubuntu 24.04 + Wine 9.0 完美运行《文明5》中文版:DXVK配置全攻略
  • 特斯拉 FSD 芯片架构揭秘:如何通过专用化设计超越英伟达 Xavier?
  • 2026年四川智能办公隔断系统深度横评:源头工厂直供与空间通透革命 - 精选优质企业推荐榜
  • 网易云音乐自动听歌打卡完整指南:快速升级到LV10的终极方法
  • rviz2 仿真控制器与真实机器人切换
  • KeyboardChatterBlocker:彻底解决机械键盘连击问题的智能解决方案
  • 从初级到高级:程序员如何规划自己的职业成长路径?
  • 保姆级教程:用薛定谔Schrödinger Maestro搞定共价对接,从蛋白配体预处理到实战筛选
  • ORA-01017错误全解析:从Oracle用户创建到权限管理的完整避坑指南
  • FreakStudio琳
  • GPU显存泄漏难定位?用eBPF+Prometheus构建大模型专属可观测栈,10分钟定位OOM根因
  • Blender结合Maps Models Importer插件:一键构建Google地图3D场景实战
  • 技术原型中的对象复制与性能优化
  • FastAPI实战:WebSocket vs Socket.IO,这回真给我整明白了!辰
  • 用Python搞定抖音点赞/收藏的‘bd-ticket-guard-client-data’参数(附完整代码)