分享一下我自己做的QT应用,这个应用还只是雏形很多功能并没有完成,但是关于界面的功能已经实现了许多,用麻雀虽小五脏俱全来形容挺合适。
感兴趣的可以下载源码:Commits · TTTTHBBBB/Coord (github.com);
按钮正反馈效果:
不同按钮的风格都是自定义的,首先要实现按钮的正反馈,也就是当点击时能得到类似成功按压的效果,这里我的思路是当鼠标点击时缩小文字的大小,在释放时再绘制原本大小的文字。
CoordBtnBase是按钮的基类,drawText负责修改文本的字体大小。所有的特殊动画效果按钮都继承自CoordBtnBase。
void CoordBtnBase::drawText(QRect rect, QPainter &painter,FEEDBACK type,QString str)
{
if(str.isEmpty()) return ;
int fontsize = this->getFontsize(rect,str);
if(type == FEEDBACK::PRESS){//需要修正
fontsize = qMax(1,fontsize- adjust);
}
painter.setFont(QFont("微软雅黑",fontsize));
painter.drawText(rect,Qt::AlignCenter,str);
}
鼠标悬浮动画一:
绘制思路是当点鼠标进入时,在按钮的底部中心点开始绘制半圆形,因为圆形的绘制使用的是drawEllipse函数在矩形中绘制圆形,drawEllipse在绘制时是从矩形的左上角开始绘制的,所以在计算矩形位置的时实际上是不断的调整绘制矩形的左上角位置和矩形的大小,需要注意的是,矩形的大小计算不能直接使用按钮的最长边框大小,这样绘制的圆形可能无法填充整个矩形按钮区域。
qreal match = qMax(this->width(),this->height());
qreal radius = std::sqrt(match*match*2);
void GlimmerButton::CoordBtnInit()
{
setAttribute(Qt::WA_StyledBackground);
this->installEventFilter(this);
EnterAnimation = new QPropertyAnimation(this,"showradius");
EnterAnimation->setDuration(600);
EnterAnimation->setEasingCurve(QEasingCurve::InOutBack);
LeaveAnimation = new QPropertyAnimation(this,"displayradius");
LeaveAnimation->setDuration(600);
LeaveAnimation->setEasingCurve(QEasingCurve::InOutBack);
}
void GlimmerButton::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
int borderwidth = 2;
QRect borderRect = this->rect().adjusted(1,1,-1,-1);
QPen pen;
if(borderRect.width()<=0 || borderRect.height()<=0 || !borderRect.isValid()) return ;
switch (type)
{
case MOUSETYPE::LEAVE:
{
//绘制背景颜色
painter.setBrush(QColor(0,0,0));
painter.setPen(Qt::NoPen);
painter.drawRect(this->rect());
//绘制消失动画
painter.setBrush(QColor(0,170,107));
QPointF pointf((this->width()-displayradius)/2.0,this->height()-displayradius/2.0);
painter.drawEllipse(pointf,displayradius,displayradius);
//绘制边框
pen.setColor(QColor(0,170,107));
pen.setStyle(Qt::SolidLine);
pen.setWidth(borderwidth);
painter.setPen(pen);
painter.setBrush(Qt::NoBrush);
painter.drawRect(borderRect);
break;
}
case MOUSETYPE::ENTER:
{
//绘制背景颜色
painter.setBrush(QColor(0,170,107));
painter.setPen(Qt::NoPen);
painter.drawRect(this->rect());
//绘制出现动画
painter.setBrush(QColor(0,0,0));
QPointF pointf((this->width()-showradius)/2.0,this->height()-showradius/2.0);
painter.drawEllipse(pointf,showradius,showradius);
//绘制文本
pen.setColor(QColor(0,170,107));
pen.setStyle(Qt::SolidLine);
pen.setWidth(borderwidth);
painter.setPen(pen);
painter.setBrush(Qt::NoBrush);
break;
}
default:
break;
}
//文本绘制
if(matchtext.isEmpty()) return ;
drawText(borderRect,painter,PressorReless,matchtext);
}
鼠标悬浮动画二:
按钮边框的流动效果,思路起始很简单分层绘制背景和文本区域,在widget上设置两块颜色区域作为背景将文本矩形区域放置在背景上面遮住中心的部分,只留下边框;随着时间不断的调整两块区域的宽度这样就形成类似流动的效果。
#include <QPainter>
#include <QPainterPath>
#include "glowbutton.h"
GlowButton::GlowButton(QWidget *parent)
: CoordBtnBase(parent)
{
this->CoordBtnInit();
}
void GlowButton::CoordBtnInit()
{
this->installEventFilter(this);
connect(&animTimer, &QTimer::timeout, this, &GlowButton::updateAnimation);
animTimer.start(15);
offset = 0;
}
void GlowButton::setIiterals(QPainter &painter)
{
if(matchtext.isEmpty()) return ;
painter.save();
QPainterPath path;
path.addRoundedRect(this->borderRect(), borderRadius, borderRadius);
painter.setClipPath(path);
painter.setPen(fontColor);
drawText(this->rect(),painter,PressorReless,matchtext);
painter.restore();
}
QRect GlowButton::borderRect() const
{
int btn_x = borderWidth;
int btn_y = borderWidth;
int btn_width = width() - borderWidth * 2;
int btn_height = height() - borderWidth * 2;
return QRect(btn_x, btn_y, btn_width, btn_height);
}
void GlowButton::drawForeground(QPainter &painter)
{
painter.save();
pixmap = getForegroundPixmap();
QRect leftRect(0, 0, width() - offset, height());
QRect rightRect(width() - offset, 0, offset, height());
QPixmap leftPixmap = pixmap.copy(leftRect);
QPixmap rightPixmap = pixmap.copy(rightRect);
painter.drawPixmap(0, 0, rightPixmap);
painter.drawPixmap(offset, 0, leftPixmap);
painter.restore();
}
QPixmap GlowButton::getForegroundPixmap() const
{//绘制渐变色背景,背景是透明的,后续的绘制会覆盖整个背景只留下预先设置的边框
QPixmap pixmap(this->width(), this->height());
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
painter.setPen(Qt::NoPen);
painter.setRenderHint(QPainter::Antialiasing);
QLinearGradient gradient(0, 0,this->width(), 0);
gradient.setColorAt(0, QColor(0, 164, 128, 230));
gradient.setColorAt(0.166, QColor(13, 88, 166, 230));
gradient.setColorAt(0.333, QColor(118, 8, 170, 230));
gradient.setColorAt(0.5, QColor(255, 144, 0, 230));
gradient.setColorAt(0.666, QColor(255, 255, 0, 230));
gradient.setColorAt(0.833, QColor(165, 239, 0, 230));
gradient.setColorAt(1, QColor(83, 223, 0, 230));
painter.setBrush(gradient);
painter.drawRect(0, 0, width(), height());
painter.end();
return pixmap;
}
void GlowButton::animForwardRun()
{
animTimer.start();
}
void GlowButton::updateAnimation()
{
if (animTimer.isActive()) {
offset += 2;
if (offset > width()) {
offset = 0;
}
update();
}
}
void GlowButton::showEvent(QShowEvent *event)
{
QPushButton::showEvent(event);
animTimer.start();
}
bool GlowButton::eventFilter(QObject *watched, QEvent *event)
{
switch (event->type())
{
case QEvent::Enter:
{
animTimer.start();
break;
}
case QEvent::Leave:
{
animTimer.stop();
break;
}
case QEvent::MouseButtonPress:
{
PressorReless = FEEDBACK::PRESS;
update();
break;
}
case QEvent::MouseButtonRelease:
{
PressorReless = FEEDBACK::RELESS;
update();
break;
}
default:
break;
}
return QObject::eventFilter(watched, event);
}
void GlowButton::setParams(int border_radius, int border_width, const QColor &font_color)
{
this->borderRadius = border_radius;
this->borderWidth = border_width;
this->fontColor = font_color;
}
void GlowButton::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event);
this->update();
}
void GlowButton::paintEvent(QPaintEvent *event)
{
QPushButton::paintEvent(event);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QPainterPath path;
path.addRoundedRect(this->borderRect(), borderRadius, borderRadius);
path.addRoundedRect(0, 0, this->width(), this->height(), borderRadius, borderRadius);
painter.setClipPath(path);
if(this->width()<=10||this->height()<=10) return ;
drawForeground(painter);
setIiterals(painter);
}