做了一个雪花飘落的程序,参考了以下两篇文章。雪花是以波浪线飘落。雪花用QToolButton添加图片制成。雪花在下落时会曲线移动,通过继承QPropertyAnimation来设置移动曲线。
参考文章:Qt实现桌面动态背景雪花飘落程序 - 零落年华 - 博客园
QT QPainter绘制图形-动态正弦曲线(水波效果)_jmlinux的博客-CSDN博客
snownode.cpp //雪花类
#include "snownode.h"
#include <QSize>
#include <QPoint>
#include <QBitmap>
#include <QPixmap>
#include <QToolButton>
#include <QApplication>
#include <QDesktopWidget>
#include <QGraphicsScene>
#include <QGraphicsView>
const int SnowNode::MAXSWOW = 19;
SnowNode::SnowNode(QWidget *parent):
QToolButton(parent),m_animation(new MyAnimation(this, "pos"))
{
//必须设置为无边框,否则可见区域和图片绘制区域将出现不重叠
setWindowFlags( Qt::FramelessWindowHint );
resize(GetSnowSize());
//对图片进行缩放
m_pixmap.load(GetImgFileName());
m_pixmap = m_pixmap.scaled(this->size(),Qt::IgnoreAspectRatio);
setHidden(true);
m_areaSize.setWidth(QApplication::desktop()->width());
m_areaSize.setHeight(QApplication::desktop()->height());
}
//初始化雪花
void SnowNode::InitSnow()
{
this->move(qrand() % m_areaSize.width(), -32);
}
//设置雪花动画
void SnowNode::FallingAnimation()
{
int x = qrand()% m_areaSize.width();
//雪花飘落全程时间
m_animation->setDuration(8000);
m_animation->setStartValue(pos());
m_animation->setEndValue(QPoint(x,m_areaSize.height()));
m_animation->start();
}
//返回雪花是否已着陆
bool SnowNode::IsLander()
{
if(this->pos().y() >= m_areaSize.height()
&& m_animation->state() == QAbstractAnimation::Stopped)
{
return true;
}
return false;
}
void SnowNode::paintEvent(QPaintEvent *event)
{
//绘制背景图片
this->setIcon(QIcon(m_pixmap));
this->setIconSize(size());
//将png图片透明部分设置为穿透
this->setMask(m_pixmap.mask());
//绘制
QToolButton::paintEvent(event);
}
//每一朵雪花的大小,采用随机生成
QSize SnowNode::GetSnowSize()
{
int x = qrand() % 10;
return x >= 6 ? QSize(32,32) : x >= 3 ? QSize(24,24) : QSize(16,16);
}
//获取雪花文件名
QString SnowNode::GetImgFileName()
{
return QString().sprintf(":/image/_%d.png", qrand()% MAXSWOW);
}
myanimation.cpp//动画类,实现移动路线
#include "myanimation.h"
#include <QtMath>
MyAnimation::MyAnimation(QObject *target, const QByteArray &propertyName, QObject *parent):
QPropertyAnimation(target, propertyName, parent)
{
}
void MyAnimation::updateCurrentTime(int currentTime)
{
if (m_path.isEmpty())
{
QPoint to = endValue().toPoint();
QPoint from = startValue().toPoint();
int height=to.y() - from.y();
//正弦曲线公式 y = A * sin(ωx + φ) + k
double w = M_PI/200; //w为角速度 ,可以理解为波浪的密度,越大密度越大
double A = 40; // A表示振幅,可以理解为水波的高度,越大高度越高
double k = from.x(); // k表示y轴偏移
int offset = to.x() - from.x();
for(int y = from.y(); y <= to.y(); y++) //x从0~w的值而改变,从而得到正弦曲线
{
double waveX = (double)(A * qSin(w * y)) + k + y * offset / height;// waveY随着x的值改变而改变,从而得到正弦曲线
if (y == from.y())
{
m_path.moveTo(waveX, y);
}
m_path.lineTo(waveX, y); //从上一个绘制点画一条线到(x,waveY);
}
}
int dura = duration();
const qreal progress = ((dura == 0) ? 1 : ((((currentTime - 1) % dura) + 1) / qreal(dura)));
qreal easedProgress = easingCurve().valueForProgress(progress);
if (easedProgress > 1.0)
{
easedProgress -= 1.0;
}
else if (easedProgress < 0)
{
easedProgress += 1.0;
}
QPointF pt = m_path.pointAtPercent(easedProgress);
updateCurrentValue(pt);
emit valueChanged(pt);
}
mainwindow.cpp//界面类,设置雪花飘落的逻辑
#include "mainwindow.h"
#include <QKeyEvent>
#include <QPixmap>
#include <QBitmap>
#include <QTime>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
//让程序无边框
setWindowFlags( Qt::FramelessWindowHint );
//让程序背景透明
setAttribute(Qt::WA_TranslucentBackground, true);
InitEnv();
m_timeManager = new QTimer(this);
connect( m_timeManager,SIGNAL(timeout()), SLOT(SnowFalling()));
m_timeManager->start(500);
m_snowPool = 100;
for(int i=0; i<m_snowPool; ++i)
{
SnowNode* node = new SnowNode(this);
Q_ASSERT(node);
m_snowWaitList.append(node);
}
}
//初始化资源
void MainWindow::InitEnv()
{
//初始化随机数种子
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
}
MainWindow::~MainWindow()
{
}
void MainWindow::keyPressEvent( QKeyEvent * event )
{
//按下esc键时,关闭
if(event->key() == Qt::Key_Escape)
{
close();
}
}
void MainWindow::AcceptShow(QList<SnowNode*> addList)
{
foreach (SnowNode* var, addList)
{
Q_ASSERT(var);
m_snowFallingList.append(var);
var->FallingAnimation();
}
QList<SnowNode*> landerList;
for(int i=0; i<m_snowFallingList.size(); )
{
if (!m_snowFallingList.at(i)->IsLander())
{
++i;
continue;
}
landerList.append(m_snowFallingList.at(i));
m_snowFallingList.removeAt(i);
}
if (landerList.size() > 0)
{
emit SnowRecycle(landerList);
}
}
void MainWindow::SnowFalling()
{
QList<SnowNode*> fallingList;
int count = qrand() % 4;
for (int i=0; i< count; i++)
{
SnowNode *node = m_snowWaitList.first();
node->InitSnow();
node->setHidden(false);
fallingList.append(node);
m_snowWaitList.pop_front();
}
if (fallingList.size() > 0)
{
AcceptShow(fallingList);
}
}
void MainWindow::SnowRecycle(QList<SnowNode*> addList)
{
for (int i=0; i<addList.size(); ++i)
{
m_snowWaitList.append(addList.at(i));
}
}
最后效果如下