参考
效果
实现
main.cpp
#include "dialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show();
return a.exec();
}
dialog.h
dialog.cpp
#include "dialog.h"
#include "flowingRayButton.h"
#include "hoveringRippleButton.h"
#pragma execution_character_set("utf-8")
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
//隐藏最大化按钮|置顶
setWindowFlags(this->windowFlags()& ~Qt::WindowMaximizeButtonHint|Qt::WindowStaysOnTopHint);
this->resize(800, 800);
this->setStyleSheet("background:#000000");
// 流动光线按钮
FlowingRayButton *a1 = new FlowingRayButton(this);
a1->setGeometry(QRect(200, 100, 440, 140));
a1->setBorder(6);
auto w =new QWidget(this);
w->setGeometry(0,270,this->width(),this->height());
w->setStyleSheet("background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #00bd39, "
"stop:0.1 #00b844, stop:0.2 #00b44f, stop:0.3 #00af59, stop:0.4 #00aa64, "
"stop:0.5 #01a66f, stop:0.6 #01a17a, stop:0.7 #019c84, stop:0.8 #01988f, "
"stop:0.9 #01939a);");
FlowingRayButton *a2 = new FlowingRayButton(this);
a2->setGeometry(QRect(200, 300, 440, 140));
/* Rborder-width 是一个自定义的属性,它不是官方的 CSS 属性;
* Rborder-width 用于样式表中不会产生任何影响。可以忽略警告Unknown property Rborder-width*/
a2->setStyleSheet("QFrame{"
" background-color: rgba(255, 255, 255,255);"
" border:none;"
" border-radius:10px;"
" Rborder-width:10px;"
"}"
"QPushButton{border-radius: 15px;color:#ffffff;}"
);
a2->setStyleSheetConfig();
new HoveringRippleButton(this,QRect(200, 500, 440, 140), QSize(440, 140));
}
Dialog::~Dialog()
{
}
flowingRayButton.h 流动光线按钮
#ifndef FLOWINGRAYBUTTON_H
#define FLOWINGRAYBUTTON_H
#include <QFrame>
#include <QPushButton>
#include <QWidget>
#include <QLineEdit>
#include <QGraphicsBlurEffect>
#include <QRect>
#include <QSize>
#include <Qt>
#include <QTimer>
#include <QParallelAnimationGroup>
#include <QPropertyAnimation>
#include <QEasingCurve>
#include <QPoint>
#include <QSequentialAnimationGroup>
#include <QAbstractAnimation>
#include <QRegularExpression>
#include <QBrush>
#include <QColor>
#include <QCursor>
#include <QFont>
#include <QPainter>
#include <QPainterPath>
#include <QLinearGradient>
#include <QDebug>
// 流动光线按钮
class FlowingRayButton : public QFrame
{
Q_OBJECT
public:
FlowingRayButton(QWidget *parent = nullptr);
~FlowingRayButton();
void setBorder(int border = 5); // 设置按钮在QFrame中的大小
void setStyleSheetConfig(); // 根据样式重新配置(解析样式设置)
QPushButton *getFlowingRayButton();
private:
QPushButton *m_pPushButton;
int border_radius; // 边框圆角
int border; // 边界
QTimer *m_pTimer;
int rect_1_offset; //
int rect_2_offset;
int rect_1_start;
int rect_2_start;
int init_x;
int flag;
void initUI();
void initAnimationConfig(); // 初始化动画配置
void enterEvent(QEvent *event) override;//重写处理鼠标光标进入部件的事件
void leaveEvent(QEvent *event) override;//重写处理鼠标光标离开部件的事件
void paintEvent(QPaintEvent *event) override;//重写绘制事件
private slots:
void offsetUpdate(); // 偏移更新
};
#endif // FLOWINGRAYBUTTON_H
flowingRayButton.cpp 流动光线按钮
#include "flowingRayButton.h"
// 流动光线按钮
FlowingRayButton::FlowingRayButton(QWidget *parent)
: QFrame(parent)
{
border_radius=10;
border=5;
initUI();
}
FlowingRayButton::~FlowingRayButton()
{
}
void FlowingRayButton::initUI()
{
this->resize(300, 100);
this->setStyleSheet("QFrame{"
" background-color: rgba(255, 255, 255,0);"
" border:none;"
" border-radius:10px;"
" Rborder-width:5px;"
"}"
"QPushButton{border-radius: 5px;color:#ff0066;}"
);
m_pPushButton = new QPushButton(this);
setBorder();
QFont font;
font.setPointSize(25);
m_pPushButton->setFont(font);
m_pPushButton->setText("Start Coding");
m_pTimer = new QTimer(this);
m_pTimer->setInterval(10); //间隔毫秒
connect(m_pTimer, SIGNAL(timeout()), this, SLOT(offsetUpdate()));
initAnimationConfig();
}
void FlowingRayButton::setBorder(int border)
{
int btn_width = this->width() - border * 2;
int btn_height = this->height() - border * 2;
int btn_x = border;
int btn_y = border;
m_pPushButton->setGeometry(QRect(btn_x, btn_y, btn_width, btn_height));
}
// 根据样式重新配置(解析样式设置)
void FlowingRayButton::setStyleSheetConfig()
{
/*匹配规则;
* 注意:正则表达式在找到第一个匹配项后就会停止查找,所以它不会再次匹配。
* 从头开始查找匹配 border-radius: 的部分,然后尝试匹配一个或多个数字字符 (\\d+),而后匹配 px;
* 使用 \\s* 来匹配可能存在的空格字符,包括零个或多个空格;
* \\d+表示匹配一个或多个数字字符
* 使用 (?P<border_radius>\\d+) 来匹配 border-radius 后面的数字,并将其命名为 border_radius;*/
QRegularExpression radius_match("border-radius:\\s*(?P<border_radius>\\d+)px;");
QRegularExpressionMatch radius_result = radius_match.match(this->styleSheet());
if (radius_result.hasMatch())
{
//提取捕获组中命名为 border_radius的内容
border_radius = radius_result.captured("border_radius").toInt();
}
QRegularExpression Rborder_width_match("Rborder-width:\\s*(?P<Rborder_width>\\d+)px;");
QRegularExpressionMatch Rborder_width_result = Rborder_width_match.match(this->styleSheet());
if (Rborder_width_result.hasMatch()) {
border = Rborder_width_result.captured("Rborder_width").toInt();
}
// 根据样式重新配置QPushButton边界
setBorder(border);
initAnimationConfig();
}
// 初始化动画配置
void FlowingRayButton::initAnimationConfig()
{
rect_1_offset = 0;
rect_2_offset = 0;
rect_1_start = 0;
rect_2_start = -this->width();
init_x = -this->width();
flag = 0;
}
// 偏移更新
void FlowingRayButton::offsetUpdate()
{
if(rect_1_offset >= this->width()&&flag==0) {
rect_1_offset = 0;
rect_1_start = init_x;
flag=1;
}
if(rect_1_offset >= this->width()*2&&flag==1) {
rect_1_offset = 0;
rect_1_start = init_x;
}
if (rect_2_offset >= this->width() * 2) {
rect_2_offset = 0;
rect_2_start = init_x;
}
rect_1_offset += 3;
rect_2_offset += 3;
update();
}
//重写处理鼠标光标进入部件的事件
void FlowingRayButton::enterEvent(QEvent *event)
{
m_pTimer->start();
// 调用父类的 enterEvent 函数,以保持默认行为
QFrame::enterEvent(event);
}
//重写处理鼠标光标离开部件的事件
void FlowingRayButton::leaveEvent(QEvent *event)
{
m_pTimer->stop();
// 调用父类的 leaveEvent 函数,以保持默认行为
QFrame::leaveEvent(event);
}
//重写绘制事件
void FlowingRayButton::paintEvent(QPaintEvent *event)
{
// 调用父类的 paintEvent 函数,以保持默认行为
QFrame::paintEvent(event);
QPainterPath path;
//添加一个带有圆角的矩形路径
path.addRoundedRect(0, 0, this->width(), this->height(), border_radius, border_radius);
QPainter painter(this);
//设置渲染提示; QPainter::Antialiasing 是一种渲染提示,用于抗锯齿绘制,使得图形边缘更加平滑
painter.setRenderHint(QPainter::Antialiasing);
//设置画笔; Qt::NoPen 表示不使用画笔,也就是不绘制边框
painter.setPen(Qt::NoPen);
//设置剪裁路径,即限制绘制区域为指定的路径范围内
painter.setClipPath(path);
//线性渐变
QLinearGradient gradient_1(rect_1_start + rect_1_offset, 0, rect_1_start + rect_1_offset + this->width(), 0);
//在(0,1)之间设置颜色的渐变过程,将一个颜色逐渐过渡到另一个
gradient_1.setColorAt(0, QColor(0, 164, 128, 230));
gradient_1.setColorAt(0.166, QColor(13, 88, 166, 230));
gradient_1.setColorAt(0.333, QColor(118, 8, 170, 230));
gradient_1.setColorAt(0.5, QColor(255, 144, 0, 230));
gradient_1.setColorAt(0.666, QColor(255, 255, 0, 230));
gradient_1.setColorAt(0.833, QColor(165, 239, 0, 230));
gradient_1.setColorAt(1, QColor(83, 223, 0, 230));
painter.setBrush(gradient_1);
painter.drawRect(rect_1_start + rect_1_offset, 0, this->width(), this->height());
QLinearGradient gradient_2(rect_2_start + rect_2_offset, 0,rect_2_start + rect_2_offset + this->width(), 0);
gradient_2.setColorAt(0, QColor(0, 164, 128, 230));
gradient_2.setColorAt(0.166, QColor(13, 88, 166, 230));
gradient_2.setColorAt(0.333, QColor(118, 8, 170, 230));
gradient_2.setColorAt(0.5, QColor(255, 144, 0, 230));
gradient_2.setColorAt(0.666, QColor(255, 255, 0, 230));
gradient_2.setColorAt(0.833, QColor(165, 239, 0, 230));
gradient_2.setColorAt(1, QColor(83, 223, 0, 230));
painter.setBrush(gradient_2);
painter.drawRect(rect_2_start + rect_2_offset, 0, this->width(), this->height());
}
QPushButton *FlowingRayButton::getFlowingRayButton()
{
return m_pPushButton;
}
hoveringRippleButton.h 悬浮波纹按钮
#ifndef HOVERINGRIPPLEBUTTON_H
#define HOVERINGRIPPLEBUTTON_H
#include <QFrame>
#include <QFrame>
#include <QPushButton>
#include <QWidget>
#include <QPainter>
#include <QPainterPath>
#include <QTimer>
#include <QMouseEvent>
#include <QPropertyAnimation>
#include <QtCore/qmath.h>
//悬浮波纹按钮
class HoveringRippleButton : public QFrame
{
Q_OBJECT
public:
HoveringRippleButton(QWidget *parent = nullptr);
HoveringRippleButton(QWidget *parent = nullptr, const QRect &geometry = QRect(), const QSize &minSize = QSize());
~HoveringRippleButton();
private:
QRect geometry;
QSize minSize;
QPushButton *m_pPushButton;
int corner_radius; // 按钮的圆角半径
int radius_var; // 半径变化值
int radius; // 起始半径
qreal max_radius; // 最大半径
QPoint center; // 鼠标点击坐标
QColor color; // 填充颜色
int msec; // 定时时间
QTimer *m_pTimer;
void initUI();
void initAnimationConfig(); // 初始化动画配置
void enterEvent(QEvent *event) override;//重写处理鼠标光标进入部件的事件
void leaveEvent(QEvent *event) override;//重写处理鼠标光标离开部件的事件
void paintEvent(QPaintEvent *event) override;//重写绘制事件
private slots:
void incRadius();
void decRadius();
};
#endif // HOVERINGRIPPLEBUTTON_H
hoveringRippleButton.cpp 悬浮波纹按钮
#include "hoveringRippleButton.h"
#pragma execution_character_set("utf-8")
//悬浮波纹按钮
HoveringRippleButton::HoveringRippleButton(QWidget *parent)
: QFrame(parent)
{
geometry=QRect(0,0,100,50);
minSize =QSize(100,50);
initUI();
}
HoveringRippleButton::HoveringRippleButton(QWidget *parent, const QRect &geometry, const QSize &minSize)
: QFrame(parent), geometry(geometry), minSize(minSize)
{
initUI();
}
HoveringRippleButton::~HoveringRippleButton()
{
}
void HoveringRippleButton::initUI()
{
setMinimumSize(minSize);
setStyleSheet("QFrame{"
" background-color: rgb(46, 22, 177);"
" border:none;"
" border-radius:10px;"
"}"
"QPushButton{"
" background-color: rgba(255, 255, 255, 0);"
" color: rgb(255, 255, 255);"
"}");
m_pPushButton = new QPushButton(this);
m_pPushButton->setMinimumSize(minSize);
m_pPushButton->setFixedSize(QSize(width(), height()));
QFont font;
font.setPointSize(25);
m_pPushButton->setFont(font);
m_pPushButton->setText("悬浮会变色喔");
setGeometry(geometry);
initAnimationConfig();
}
void HoveringRippleButton::initAnimationConfig()
{
corner_radius = 10; // 按钮的圆角半径
radius_var = 2; // 半径变化值
radius = 0; // 起始半径
max_radius = qSqrt(width() * width() + height() * height()); // 最大半径
center = QPoint(); // 鼠标点击坐标
color = QColor(255, 89, 0); // 填充颜色
msec = 10; // 定时时间
m_pTimer = new QTimer(this);
m_pTimer->setInterval(msec);
connect(m_pTimer,SIGNAL(timeout()), this, SLOT(incRadius()));
}
//重写处理鼠标光标进入部件的事件
void HoveringRippleButton::enterEvent(QEvent *event)
{
// 调用父类的 enterEvent 函数,以保持默认行为
QFrame::enterEvent(event);
/* QCursor::pos() 获取当前鼠标的全局坐标
* mapFromGlobal()将全局坐标转换为窗口内部相对坐标*/
center = mapFromGlobal(QCursor::pos());
m_pTimer->disconnect();
connect(m_pTimer, SIGNAL(timeout()), this, SLOT(incRadius()));
m_pTimer->start();
}
//重写处理鼠标光标离开部件的事件
void HoveringRippleButton::leaveEvent(QEvent *event)
{
// 调用父类的 leaveEvent 函数,以保持默认行为
QFrame::leaveEvent(event);
center = mapFromGlobal(QCursor::pos());
m_pTimer->disconnect();
connect(m_pTimer, SIGNAL(timeout()), this, SLOT(decRadius()));
m_pTimer->start();
}
//重写绘制事件
void HoveringRippleButton::paintEvent(QPaintEvent *event)
{
// 调用父类的 paintEvent 函数,以保持默认行为
QFrame::paintEvent(event);
if (center.isNull()) {
return;
}
QPainter painter(this);
//设置渲染提示; QPainter::Antialiasing 是一种渲染提示,用于抗锯齿绘制,使得图形边缘更加平滑
painter.setRenderHint(QPainter::Antialiasing);
QBrush brush(color);
painter.setBrush(brush);
//设置画笔; Qt::NoPen 表示不使用画笔,也就是不绘制边框
painter.setPen(Qt::NoPen);
QPainterPath path;
//添加一个带有圆角的矩形路径
path.addRoundedRect(rect(), corner_radius, corner_radius);
//设置剪裁路径,即限制绘制区域为指定的路径范围内
painter.setClipPath(path);
//绘制椭圆(长轴==长轴,就圆形)
painter.drawEllipse(center, radius, radius);
}
void HoveringRippleButton::incRadius()
{
radius += radius_var;
if (radius > max_radius) {
m_pTimer->stop();
return;
}
update();
}
void HoveringRippleButton::decRadius()
{
radius -= radius_var;
if (radius < 0) {
m_pTimer->stop();
return;
}
update();
}
模糊知识点
- 使用
QRegularExpression
对象的match()
函数,可以将正则表达式应用于目标文本,并检查是否存在匹配项;QRegularExpressionMatch
类存储匹配结果,并提供方法来访问和提取捕获组中的具体内容。
/*匹配规则;
* 注意:正则表达式在找到第一个匹配项后就会停止查找,所以它不会再次匹配。
* 从头开始查找匹配 border-radius: 的部分,然后尝试匹配一个或多个数字字符 (\\d+),而后匹配 px;
* 使用 \\s* 来匹配可能存在的空格字符,包括零个或多个空格;
* \\d+表示匹配一个或多个数字字符
* 使用 (?P<border_radius>\\d+) 来匹配 border-radius 后面的数字,并将其命名为 border_radius;*/
QRegularExpression radius_match("border-radius:\\s*(?P<border_radius>\\d+)px;");
QRegularExpressionMatch radius_result = radius_match.match(this->styleSheet());
if (radius_result.hasMatch())
{
//提取捕获组中命名为 border_radius的内容
border_radius = radius_result.captured("border_radius").toInt();
}
QPainterPath
用于绘制复杂图形路径的类;QPainter::setClipPath()
设置剪裁路径,即限制绘制区域为指定的路径范围内
QPainterPath path;
//添加一个带有圆角的矩形路径
path.addRoundedRectaddRoundedRect(qreal x, qreal y, qreal w, qreal h, qreal xRadius, qreal yRadius, Qt::SizeMode mode);
QPainter painter(this);
//设置渲染提示; QPainter::Antialiasing 是一种渲染提示,用于抗锯齿绘制,使得图形边缘更加平滑
painter.setRenderHint(QPainter::Antialiasing);
//设置画笔; Qt::NoPen 表示不使用画笔,也就是不绘制边框
painter.setPen(Qt::NoPen);
//设置剪裁路径,即限制绘制区域为指定的路径范围内
painter.setClipPath(path);
- **
QCursor::pos()
获取当前鼠标的全局坐标
mapFromGlobal()
将全局坐标转换为窗口内部相对坐标**
QPoint mapFromGlobal(QCursor::pos());
源码
[Gitee:06AnimationButton C++复刻:[流光按钮]+[悬浮波纹按钮]](https://gitee.com/ljc8/all_-qt_-project/tree/master/06AnimationButton)