一、概述
最近想做一个炫酷的悬浮式菜单,考虑到菜单展开和美观,所以考虑学习下Qt的动画系统和状态机内容,打开QtCreator的示例教程浏览了下,大致发现教程中2D Painting
程序和Animated Tiles
程序有所帮助,如下图所示,这两个demo讲述了怎么做一个展开动画,感兴趣的同学也可以直接参考
有了这两个demo之后,就可以开始动工写咱们自己的程序。
二、效果展示
如下两幅图就是作者失效的两个悬浮菜单效果图,展示图1代码已上传至CSDN,不需要积分即可下载,效果图2代码暂时不开源,有需要的朋友可以进一步咨询
基础圆形菜单功能,代码已上传CSDN - Qt 失效的 PC 端环形菜单、悬浮球菜单、展开动画
高级悬浮球菜单、支持二级菜单打开
三、实现代码
实现文件比较简单,只有头文件和实现文件,这里先主要放出头文件,然后讲解实现思路,具体实现细节可以通过下载源码进行具体了解
1、菜单项
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;
};
2、悬浮球
悬浮球为菜单入口,继承自菜单项,与菜单项有相似功能
class QVariantAnimation;
class QPropertyAnimation;
class PopRingMenu : public PopRingItem
{
Q_OBJECT
public:
PopRingMenu(QWidget *parent = 0);
~PopRingMenu();
signals:
void DoubleClicked();
public:
void SetActions(const QVector<QAction *> & acts);
void SetIcons(const QVector<QString> & 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<PopRingItem *> m_items;
};
3、关键点
初始化动画对象,指定动画时长和动画起始、终止值
动画具体实现函数未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;
}
}
}
四、相关文章
- qt 之菜单项定制
- Qt 之 QAbstractItemView 右键菜单
- Qt 弹出式菜单阴影
- Qt 之自定义 QLineEdit 右键菜单
- Qt 之股票组件 - 自选股 -- 列表可以拖拽、右键常用菜单