最近在做一个小项目,需要实现网易云音乐背景墙的轮换效果,如下:
以下是.h代码:
#ifndef ANIMATEDWALLWG_H
#define ANIMATEDWALLWG_H
#include
#include
#include
#include
#define LRWIDTH 35 //左右两个点击按钮的宽度
#define SCALE 0.86 //旁边两个小Label占中间大Label的比例
#define COVER 30 //中间大Label挡住两边小Label的部分
#define DURATION 400 //动画持续时间
#define EASINGTIME 0.5 //中间缓冲时间
#define EASINGCURVE QEasingCurve::InOutCubic //缓和曲线 InOutSine InOutQuart InOutCubic
class AnimatedWallWG : public QWidget
{
Q_OBJECT
enum { COUNT = 6 };
public:
explicit AnimatedWallWG(QWidget *parent = 0);
//初始化
void initVar();
void initWallpaper();
void initLRButton();
void setWallRect();
void setLRButtonRect();
void setWallpaper(QLabel *pWall, int i);
void wallIndex(bool b, int n, int &i, int &j, int &k, int &l);
//本类实例
static AnimatedWallWG* Instance(QWidget *parent = 0)
{
if (m_pInstance == NULL)
{
m_pInstance = new AnimatedWallWG(parent);
}
return m_pInstance;
}
protected slots:
void moveToLeft();
void moveToRight();
void onValueChanged(QVariant variant);
void finished() { m_bAnimation = true; }
protected:
bool eventFilter(QObject *watched, QEvent *event);
void timerEvent(QTimerEvent *event);
void paintEvent(QPaintEvent *event);
private:
static AnimatedWallWG *m_pInstance;
//正中间图片在链表中的索引
int m_iWall; //控件的索引
int m_iRaise; //置顶控件索引
int m_idTimer;
bool m_bAnimation; //是否可以轮换
//QLabel
QLabel *m_pWall[COUNT];
QRect m_rtWall[3]; //没变化前的状态
QRect m_rtLChange[5]; //从左到右变化过程中的状态
QRect m_rtRChange[6]; //从右到左变化过程中的状态
//QPushButton
QPushButton *m_pLRButton[2]; //左右移动的两个点击按钮
};
#endif // ANIMATEDWALLWG_H
以下是.cpp代码:
#include "AnimatedWallWG.h"
#include
#include
#include
#include
AnimatedWallWG *AnimatedWallWG::m_pInstance = NULL;
/***************************************************
* 函数名称: AnimatedWallWG
* 功 能: 构造函数 初始化UI、变量
* 输入参数: parent 父类指针
* 输出参数:
* *************************************************/
AnimatedWallWG::AnimatedWallWG(QWidget *parent) :
QWidget(parent)
{
//1. 初始化
initVar();
initWallpaper();
initLRButton();
//2. 开启定时器8秒轮换一次
m_idTimer = startTimer(8000);
//3. 安装事件过滤器捕获窗口大小改变事件
this->installEventFilter(this);
}
/***************************************************
* 函数名称: initVar
* 功 能: 初始化变量
* 输入参数:
* 输出参数:
* *************************************************/
void AnimatedWallWG::initVar()
{
m_iWall = 0;
m_iRaise = 0;
m_idTimer = 0;
m_bAnimation = true;
}
/***************************************************
* 函数名称: initWallpaper
* 功 能: 初始化背景墙
* 输入参数:
* 输出参数:
* *************************************************/
void AnimatedWallWG::initWallpaper()
{
//创建COUNT个Wall并且设置背景图
for (int i = 0; i < COUNT; i++)
{
m_pWall[i] = new QLabel(this);
setWallpaper(m_pWall[i], i);
}
//默认索引为1的Wall置顶
m_pWall[1]->raise();
//为了好看暂时把没用的Wall先移走
m_pWall[3]->move(1920, 0);
m_pWall[4]->move(1920, 0);
m_pWall[5]->move(1920, 0);
}
/***************************************************
* 函数名称: initLRButton
* 功 能: 初始化左右两个点击按钮
* 输入参数:
* 输出参数:
* *************************************************/
void AnimatedWallWG::initLRButton()
{
//创建两个移动按钮
m_pLRButton[0] = new QPushButton(this);
m_pLRButton[1] = new QPushButton(this);
m_pLRButton[0]->hide();
m_pLRButton[1]->hide();
connect(m_pLRButton[0], SIGNAL(clicked()), this, SLOT(moveToRight()));
connect(m_pLRButton[1], SIGNAL(clicked()), this, SLOT(moveToLeft()));
//设置按钮的图标
QString style1 = QString("QPushButton{border: 0px; background: url(:/Image/MoveL.png) center no-repeat;}"
"QPushButton:hover{background-color: rgba(0, 0, 0, 30%);}"
"QPushButton:pressed{background-color: rgba(0, 0, 0, 50%);}");
m_pLRButton[0]->setStyleSheet(style1);
QString style2 = QString("QPushButton{border: 0px; background: url(:/Image/MoveR.png) center no-repeat;}"
"QPushButton:hover{background-color: rgba(0, 0, 0, 30%);}"
"QPushButton:pressed{background-color: rgba(0, 0, 0, 50%);}");
m_pLRButton[1]->setStyleSheet(style2);
}
/***************************************************
* 函数名称: setWallRect
* 功 能: 设置动画移动的几个范围
* 输入参数:
* 输出参数:
* *************************************************/
void AnimatedWallWG::setWallRect()
{
int w, h;
//中间的矩形geometry
h = height();
w = width() / 2.0;
m_rtWall[1] = QRect(w / 2, 0, w, h);
//左边矩形的geometry、右边矩形的geometry
h = height() * SCALE;
w = width() / 2.0 * SCALE;
m_rtWall[0] = QRect(m_rtWall[1].left() - w + COVER, height() - h, w, h);
m_rtWall[2] = QRect(m_rtWall[1].right() - COVER, height() - h, w, h);
//从左到右变化过程中的五个geometry
m_rtLChange[0] = QRect(0 - w, height() - h, w, h);
m_rtLChange[1] = QRect(m_rtLChange[0].x() + w, height() - h, w, h);
m_rtLChange[2] = QRect(m_rtLChange[1].x() + w - COVER, height() - h, w, h);
m_rtLChange[3] = QRect(m_rtLChange[2].x() + w - COVER, height() - h, w, h);
m_rtLChange[4] = QRect(m_rtWall[2].x() + w - COVER, height() - h, w, h);
//从右到左变化过程中的五个geometry
m_rtRChange[0] = QRect(width(), height() - h, w, h);
m_rtRChange[1] = QRect(m_rtRChange[0].x() - w, height() - h, w, h);
m_rtRChange[2] = QRect(m_rtRChange[1].x() - w + COVER, height() - h, w, h);
m_rtRChange[3] = QRect(m_rtRChange[2].x() - w + COVER, height() - h, w, h);
m_rtRChange[4] = QRect(m_rtWall[0].x() - w + COVER, height() - h, w, h);
//根据索引得到当前活动的三个Wall的下标
int i, j, k, l;
wallIndex(true, m_iWall, i, j, k, l);
m_pWall[i]->setGeometry(m_rtWall[0]);
m_pWall[j]->setGeometry(m_rtWall[1]);
m_pWall[k]->setGeometry(m_rtWall[2]);
}
/***************************************************
* 函数名称: setLRButtonRect
* 功 能: 设置左右移动两个按钮的大小及位置
* 输入参数:
* 输出参数:
* *************************************************/
void AnimatedWallWG::setLRButtonRect()
{
//设置按钮的大小
int w = LRWIDTH;
int h = height() * SCALE;
m_pLRButton[0]->setGeometry(0, height() - h, w, h);
m_pLRButton[1]->setGeometry(width() - w, height() - h, w, h);
}
/***************************************************
* 函数名称: setWallpaper
* 功 能: 设置墙纸
* 输入参数:
* 输出参数:
* *************************************************/
void AnimatedWallWG::setWallpaper(QLabel *pWall, int i)
{
if (pWall == NULL) return;
QString style = QString("QLabel{border: 0px; border-image: url(:/Image/Wall%1.png);}"
"QLabel:hover{background-color: rgba(255, 255, 255, 30%);}").arg(i);
pWall->setStyleSheet(style);
}
/***************************************************
* 函数名称: wallIndex
* 功 能: 通过索引得到相对应的控件的下标
* 输入参数: b 正反向
* n 当前索引
*
* 输出参数: i 左边控件的下标
* j 中间控件的下标
* k 右边控件的下标
* l 待出来控件的下标
* *************************************************/
void AnimatedWallWG::wallIndex(bool b, int n, int &i, int &j, int &k, int &l)
{
switch (n)
{
case 0: i = 0; j = 1; k = 2; l = b ? 3 : 5; break;
case 1: i = 1; j = 2; k = 3; l = b ? 4 : 0; break;
case 2: i = 2; j = 3; k = 4; l = b ? 5 : 1; break;
case 3: i = 3; j = 4; k = 5; l = b ? 0 : 2; break;
case 4: i = 4; j = 5; k = 0; l = b ? 1 : 3; break;
case 5: i = 5; j = 0; k = 1; l = b ? 2 : 4; break;
}
}
/***************************************************
* 函数名称: moveToRight
* 功 能: 从右往左移动
* 输入参数:
* 输出参数:
* *************************************************/
void AnimatedWallWG::moveToLeft()
{
//判图片断当前是否可以轮换图片
if (!m_bAnimation) return;
m_bAnimation = false;
//根据当前索引获取左、中、右、待出现的控件
int i, j, k, l;
wallIndex(true, m_iWall, i, j, k, l);
m_iRaise = k;
//创建动画
QPropertyAnimation *pAnimation[4];
//左
pAnimation[0] = new QPropertyAnimation(m_pWall[i], "geometry");
connect(pAnimation[0], SIGNAL(finished()), pAnimation[0], SLOT(deleteLater()));
pAnimation[0]->setDuration(DURATION);
pAnimation[0]->setEasingCurve(EASINGCURVE);
pAnimation[0]->setKeyValueAt(0, m_rtWall[0]);
pAnimation[0]->setKeyValueAt(EASINGTIME, m_rtRChange[3]);
pAnimation[0]->setKeyValueAt(1, m_rtRChange[4]);
//中
pAnimation[1] = new QPropertyAnimation(m_pWall[j], "geometry");
connect(pAnimation[1], SIGNAL(finished()), pAnimation[1], SLOT(deleteLater()));
pAnimation[1]->setDuration(DURATION);
pAnimation[1]->setEasingCurve(EASINGCURVE);
pAnimation[1]->setKeyValueAt(0, m_rtWall[1]);
pAnimation[1]->setKeyValueAt(EASINGTIME, m_rtRChange[2]);
pAnimation[1]->setKeyValueAt(1, m_rtWall[0]);
//右
pAnimation[2] = new QPropertyAnimation(m_pWall[k], "geometry");
connect(pAnimation[2], SIGNAL(finished()), pAnimation[2], SLOT(deleteLater()));
connect(pAnimation[2], SIGNAL(valueChanged(QVariant)), this, SLOT(onValueChanged(QVariant)));
pAnimation[2]->setDuration(DURATION);
pAnimation[2]->setEasingCurve(EASINGCURVE);
pAnimation[2]->setKeyValueAt(0, m_rtWall[2]);
pAnimation[2]->setKeyValueAt(EASINGTIME, m_rtRChange[1]);
pAnimation[2]->setKeyValueAt(1, m_rtWall[1]);
//待出现
pAnimation[3] = new QPropertyAnimation(m_pWall[l], "geometry");
connect(pAnimation[3], SIGNAL(finished()), pAnimation[3], SLOT(deleteLater()));
pAnimation[3]->setDuration(DURATION);
pAnimation[3]->setEasingCurve(EASINGCURVE);
pAnimation[3]->setKeyValueAt(0, m_rtRChange[0]);
pAnimation[3]->setKeyValueAt(EASINGTIME, m_rtRChange[0]);
pAnimation[3]->setKeyValueAt(1, m_rtWall[2]);
//组
QParallelAnimationGroup *pGroup = new QParallelAnimationGroup(this);
connect(pGroup, SIGNAL(finished()), pGroup, SLOT(deleteLater()));
connect(pGroup, SIGNAL(finished()), this, SLOT(finished()));
pGroup->addAnimation(pAnimation[0]);
pGroup->addAnimation(pAnimation[1]);
pGroup->addAnimation(pAnimation[2]);
pGroup->addAnimation(pAnimation[3]);
pGroup->start();
//序号++
if (++m_iWall >= COUNT) m_iWall = 0;
}
/***************************************************
* 函数名称: moveToRight
* 功 能: 从左往右移动
* 输入参数:
* 输出参数:
* *************************************************/
void AnimatedWallWG::moveToRight()
{
//判图片断当前是否可以轮换图片
if (!m_bAnimation) return;
m_bAnimation = false;
//根据当前索引获取左、中、右、待出现的控件
int i, j, k, l;
wallIndex(false, m_iWall, i, j, k, l);
m_iRaise = i;
//创建动画
QPropertyAnimation *pAnimation[4];
//左
pAnimation[0] = new QPropertyAnimation(m_pWall[i], "geometry");
connect(pAnimation[0], SIGNAL(finished()), pAnimation[0], SLOT(deleteLater()));
connect(pAnimation[0], SIGNAL(valueChanged(QVariant)), this, SLOT(onValueChanged(QVariant)));
pAnimation[0]->setDuration(DURATION);
pAnimation[0]->setEasingCurve(EASINGCURVE);
pAnimation[0]->setKeyValueAt(0, m_rtWall[0]);
pAnimation[0]->setKeyValueAt(EASINGTIME, m_rtLChange[1]);
pAnimation[0]->setKeyValueAt(1, m_rtWall[1]);
//中
pAnimation[1] = new QPropertyAnimation(m_pWall[j], "geometry");
connect(pAnimation[1], SIGNAL(finished()), pAnimation[1], SLOT(deleteLater()));
pAnimation[1]->setDuration(DURATION);
pAnimation[1]->setEasingCurve(EASINGCURVE);
pAnimation[1]->setKeyValueAt(0, m_rtWall[1]);
pAnimation[1]->setKeyValueAt(EASINGTIME, m_rtLChange[2]);
pAnimation[1]->setKeyValueAt(1, m_rtWall[2]);
//右
pAnimation[2] = new QPropertyAnimation(m_pWall[k], "geometry");
connect(pAnimation[2], SIGNAL(finished()), pAnimation[2], SLOT(deleteLater()));
pAnimation[2]->setDuration(DURATION);
pAnimation[2]->setEasingCurve(EASINGCURVE);
pAnimation[2]->setKeyValueAt(0, m_rtWall[2]);
pAnimation[2]->setKeyValueAt(EASINGTIME, m_rtLChange[3]);
pAnimation[2]->setKeyValueAt(1, m_rtLChange[4]);
//待出现
pAnimation[3] = new QPropertyAnimation(m_pWall[l], "geometry");
connect(pAnimation[3], SIGNAL(finished()), pAnimation[3], SLOT(deleteLater()));
pAnimation[3]->setDuration(DURATION);
pAnimation[3]->setEasingCurve(EASINGCURVE);
pAnimation[3]->setKeyValueAt(0, m_rtLChange[0]);
pAnimation[3]->setKeyValueAt(EASINGTIME, m_rtLChange[0]);
pAnimation[3]->setKeyValueAt(1, m_rtWall[0]);
//组
QParallelAnimationGroup *pGroup = new QParallelAnimationGroup(this);
connect(pGroup, SIGNAL(finished()), pGroup, SLOT(deleteLater()));
connect(pGroup, SIGNAL(finished()), this, SLOT(finished()));
pGroup->addAnimation(pAnimation[0]);
pGroup->addAnimation(pAnimation[1]);
pGroup->addAnimation(pAnimation[2]);
pGroup->addAnimation(pAnimation[3]);
pGroup->start();
//序号--
if (--m_iWall < 0) m_iWall = COUNT - 1;
}
/***************************************************
* 函数名称: onValueChanged
* 功 能: 根据时间设置中间控件Raise
* 输入参数:
* 输出参数:
* *************************************************/
void AnimatedWallWG::onValueChanged(QVariant variant)
{
Q_UNUSED(variant);
QPropertyAnimation *pAnimation = qobject_cast(sender());
if (pAnimation == NULL) return;
if (pAnimation->currentTime() > DURATION * EASINGTIME)
{
m_pLRButton[0]->raise();
m_pLRButton[1]->raise();
m_pWall[m_iRaise]->raise();
}
}
/***************************************************
* 函数名称: eventFilter
* 功 能: 事件过滤器
* 输入参数:
* 输出参数:
* *************************************************/
bool AnimatedWallWG::eventFilter(QObject *watched, QEvent *event)
{
switch ((int)event->type())
{
case QEvent::Resize:
setWallRect();
setLRButtonRect();
break;
case QEvent::Enter:
m_pLRButton[0]->show();
m_pLRButton[1]->show();
break;
case QEvent::Leave:
m_pLRButton[0]->hide();
m_pLRButton[1]->hide();
break;
}
return QWidget::eventFilter(watched, event);
}
/***************************************************
* 函数名称: timerEvent
* 功 能: 计时器
* 输入参数:
* 输出参数:
* *************************************************/
void AnimatedWallWG::timerEvent(QTimerEvent *event)
{
if (event->timerId() != m_idTimer) return;
//默认从右到左
moveToLeft();
}
/***************************************************
* 函数名称: paintEvent
* 功 能: 画背景色
* 输入参数:
* 输出参数:
* *************************************************/
void AnimatedWallWG::paintEvent(QPaintEvent *event)
{
QPainter paint(this);
paint.setPen(Qt::NoPen);
paint.setBrush(QBrush(QColor(250, 250, 252)));
paint.drawRect(rect());
QWidget::paintEvent(event);
}
最后实现效果如下:
想要源码的朋友可以到https://download.csdn.net/download/ilson_/10804647下载