做了一个雪花飘落的程序,参考了以下两篇文章。雪花是以波浪线飘落。雪花用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 + φ) + kdouble 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));}}
最后效果如下