目录
原文链接:Qt 之悬浮球菜单
最近想做一个炫酷的悬浮式菜单,考虑到菜单展开和美观,所以考虑学习下 Qt 的动画系统和状态机内容,打开 QtCreator 的示例教程浏览了下,大致发现教程中 2D Painting 程序和 Animated Tiles 程序有所帮助,如下图所示,这两个 demo 讲述了怎么做一个展开动画,感兴趣的同学也可以直接参考

有了这两个 demo 之后,就可以开始动工写咱们自己的程序。
如下两幅图就是作者失效的两个悬浮菜单效果图,展示图 1 代码已上传至 CSDN,不需要积分即可下载,效果图 2 代码暂时不开源,有需要的朋友可以进一步咨询
基础圆形菜单功能,代码已上传 CSDN - Qt 失效的 PC 端环形菜单、悬浮球菜单、展开动画
高级悬浮球菜单、支持二级菜单打开
实现文件比较简单,只有头文件和实现文件,这里先主要放出头文件,然后讲解实现思路,具体实现细节可以通过下载源码进行具体了解
PopRingItem 为菜单展开项、可以通过绑定外部 QAction 实现与普通菜单相同功能
- class PopRingItem : public QLabel
- {
- Q_OBJECT
-
- public:
- PopRingItem(QWidget *parent = 0);
- ~PopRingItem();
-
- void SetRadius(int radius);
- int GetRadius() const;
-
- void BindAction(QAction * action);
-
- signals:
- void MouseEvent(bool);
-
- protected:
- virtual void enterEvent(QEvent * event) override;
- virtual void leaveEvent(QEvent * event) override;
-
- virtual void paintEvent(QPaintEvent * event) override;
-
- protected:
- int m_iRadius = 50;
- QAction * m_actAction = nullptr;
- };
悬浮球为菜单入口,继承自菜单项,与菜单项有相似功能
- class QVariantAnimation;
- class QPropertyAnimation;
- class PopRingMenu : public PopRingItem
- {
- Q_OBJECT
-
- public:
- PopRingMenu(QWidget *parent = 0);
- ~PopRingMenu();
-
- signals:
- void DoubleClicked();
-
- public:
- void SetActions(const QVector
& acts ); - void SetIcons(const QVector
& icons ); -
- void SetAnimationEnabled(bool enabled);
- bool IsAnimationEnabled() const;
-
- void SetSlowlyFade(bool enabled);
- bool IsSlowlyFade() const;
-
- void SetDistanced(int distance);
- int GetDistanced() const;
-
- void SetStartAngle(int angle);
- int GetStartAngle() const;
-
- void SetStepAngle(int angle);
- int GetStepAngle() const;
-
- void SetNormalMenuSize(int size);
- int GetNormalMenuSize() const;
- void SetNormalItemSize(int size);
- int GetNormalItemSize() const;
-
- protected:
- virtual void enterEvent(QEvent * event) override;
- virtual void leaveEvent(QEvent * event) override;
- virtual void mouseDoubleClickEvent(QMouseEvent * event) override;
-
- virtual void timerEvent(QTimerEvent * event) override;
- virtual bool event(QEvent * event) override;
-
- private slots:
- void OnMouseEvent(bool);
-
- private:
- void UpdateActions(int msecond);
-
- void ExpandMenu();
- void CollapseMenu();
-
- void SlowlyFade();
- void QuicklyLighter();
-
- bool IsUnderMouse() const;
-
- void TryCollapseMenu();
- void KillHideTimer();
-
- private:
- int m_iDistance = 70;
- int m_iStartAngle = 0;
- int m_iStepAngle = 60;
-
- int m_iMenuSize = 70;
- int m_iItemSize = 60;
-
- int m_iTimerID = -1;
-
- QPropertyAnimation * m_pOpacityAnimation = nullptr;
- QVariantAnimation * m_pItemAnimation = nullptr;
- QVector
m_items; - };
初始化动画对象,指定动画时长和动画起始、终止值
动画具体实现函数未 UpdateAction,根据当前动画进度值在动画起始值和终止值所占比例,进行计算当前动画时刻菜单项的位置和大小
- m_pItemAnimation = new QVariantAnimation(this);
-
- m_pItemAnimation->setEasingCurve(QEasingCurve::InCubic);
- m_pItemAnimation->setStartValue(ShowMenuStartValue);
- m_pItemAnimation->setEndValue(ShowMenuEndValue);
- m_pItemAnimation->setDuration(ShowMenuDuration);
-
- connect(m_pItemAnimation, &QVariantAnimation::valueChanged, this, [this](const QVariant & v){
- UpdateActions(v.toInt());
- });
鼠标进入悬浮球时,执行展开动画
- void PopRingMenu::ExpandMenu()
- {
- if (m_pItemAnimation)
- {
- if (m_pItemAnimation->state() != QAbstractAnimation::Running
- && m_pItemAnimation->currentValue().toInt() != ShowMenuEndValue)
- {
- m_pItemAnimation->setDirection(QVariantAnimation::Forward);
- m_pItemAnimation->start();
- }
- }
- else
- {
- UpdateActions(ShowMenuEndValue);
- }
-
- KillHideTimer();
- QuicklyLighter();
- }
- 鼠标离开悬浮球时,执行收起动画,与展开动画相反方向
- 收起动画时有一个细节点,那就是鼠标 hover 在菜单项上时,也不能收起
- void PopRingMenu::CollapseMenu()
- {
- if (false == IsUnderMouse())
- {
- if (m_pItemAnimation)
- {
- m_pItemAnimation->setDirection(QVariantAnimation::Backward);
- m_pItemAnimation->start();
- }
- else
- {
- UpdateActions(ShowMenuStartValue);
- }
-
- KillHideTimer();
- SlowlyFade();
- }
- }
展开和收起动画实现细节,根据动画指定帧数,按比例进行缩放和移动菜单项
- void PopRingMenu::UpdateActions(int msecond)
- {
- int curDistance = msecond * m_iDistance / ShowMenuEndValue;
- for (int i = 0; i < m_items.size(); ++i)
- {
- PopRingItem * item = m_items.at(i);
-
- double radians = qDegreesToRadians(m_iStepAngle * i * 1.0 + m_iStartAngle);
- int offx = curDistance * qCos(radians);
- int offy = curDistance * qSin(radians);
- item->move(pos() + QPoint(offx, offy));
-
- int curSize = msecond * m_iItemSize / ShowMenuEndValue;
- item->SetRadius(curSize);
-
- item->setVisible(ShowMenuStartValue != msecond);
- };
-
- ::SetWindowPos(HWND(winId()), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
- }
悬浮球指定时间未激活时,淡出,减少对用户视觉冲击
- void PopRingMenu::SetSlowlyFade(bool enabled)
- {
- if (enabled)
- {
- if (nullptr == m_pOpacityAnimation)
- {
- m_pOpacityAnimation = new QPropertyAnimation(this, "opacity");
- m_pOpacityAnimation->setEasingCurve(QEasingCurve::OutCubic);
- m_pOpacityAnimation->setStartValue(SlowlyStartValue);
- m_pOpacityAnimation->setEndValue(SlowLyEndValue);
- m_pOpacityAnimation->setDuration(SlowlyFadeDuration);
- }
- }
- else
- {
-
- if (m_pOpacityAnimation)
- {
- delete m_pOpacityAnimation;
- m_pOpacityAnimation = nullptr;
- }
- }
- }
值得一看的优秀文章:
如果您觉得文章不错,不妨给个打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!!
很重要 -- 转载声明
本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords
如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。