测试环境:win10、Qt Creator 4.11.1、5.14.2版本SDK
一、实际效果:
二、实现原理:
1.序列帧图是将一系列动作放在一张图中展示,一般每幅图大小相等(方便程序处理)。
2.根据每一帧大小定时绘制一幅图。
3.在paintEvent使用QPainter的drawPixmap进行绘制。
三、源代码
sequenceFrameAnimation.h
#ifndef SEQUENCEFRAMEANIMATION_H
#define SEQUENCEFRAMEANIMATION_H
#include <QWidget>
#include <QLabel>
#include <QTimer>
#include <QPixmap>
class SequenceFrameAnimation : public QWidget
{
Q_OBJECT
public:
enum AnimationSequence
{
BeginToEnd,
EndToBegin,
};
explicit SequenceFrameAnimation(QWidget *parent = nullptr);
~SequenceFrameAnimation();
bool setSequenceFrameAnimation(const QString& fileName, int frameWidth, int frameHeight, int frameRate);
bool setAnimationSequence(AnimationSequence sequence);
bool setStartFrameIndex(int index = 0);
void setScaled(const QSize& size); //设置缩放大小时,长宽必须是每一帧长宽的整数倍
void setLoopEnabled(bool enabled); //设置循环播放
void setSpeedUpEnabled(bool enabled); //动画启动加速过程
bool start(); //开始
void stop(); //结束
signals:
private slots:
void onTimeout();
void onSpeedUp();
protected:
void paintEvent(QPaintEvent *event) override;
private:
QPixmap m_sequenceFramePixmap;
QSize m_scaledSize;
QRect m_currentFrameRect;
QTimer m_timer;
QTimer m_speedUpTimer;
const int m_maxSpeedUpCount;
int m_currentSpeedUpCount;
int m_perFrameWidth;
int m_perFrameHeight;
int m_frameRate;
int m_frameIndex;
int m_row;
int m_col;
AnimationSequence m_animationSequence;
bool m_loop;
bool m_needSpeedUp;
private:
int getCurrentSpeedUpTime();
};
#endif // SEQUENCEFRAMEANIMATION_H
sequenceFrameAnimation.cpp
#include "sequenceframeanimation.h"
#include <QPainter>
#include <QStyleOption>
#include <QtMath>
#include <QDebug>
#define SPEEDUP_INTERVAL 10
SequenceFrameAnimation::SequenceFrameAnimation(QWidget *parent) : QWidget(parent),
m_maxSpeedUpCount(10),
m_currentSpeedUpCount(1),
m_perFrameWidth(0),
m_perFrameHeight(0),
m_frameRate(0),
m_frameIndex(0),
m_animationSequence(BeginToEnd),
m_loop(false),
m_needSpeedUp(false)
{
setWindowFlags(Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint);
setAttribute(Qt::WA_TranslucentBackground, true);
connect(&m_timer, &QTimer::timeout, this, &SequenceFrameAnimation::onTimeout);
connect(&m_speedUpTimer, &QTimer::timeout, this, &SequenceFrameAnimation::onSpeedUp);
resize(100, 100);
}
SequenceFrameAnimation::~SequenceFrameAnimation()
{
}
bool SequenceFrameAnimation::setSequenceFrameAnimation(const QString& fileName, int frameWidth, int frameHeight, int frameRate)
{
bool ret = (fileName.isEmpty() ? false : true) && (frameWidth > 0) && (frameHeight > 0) && (frameRate > 0);
if(ret)
{
m_sequenceFramePixmap.load(fileName);
m_col = m_sequenceFramePixmap.width() / frameWidth;
m_row = m_sequenceFramePixmap.height() / frameHeight;
m_perFrameWidth = frameWidth;
m_perFrameHeight = frameHeight;
m_frameRate = frameRate;
m_currentFrameRect = QRect(0, 0, m_perFrameWidth, m_perFrameHeight);
m_scaledSize = m_sequenceFramePixmap.size();
resize(m_perFrameWidth, m_perFrameHeight);
}
return ret;
}
bool SequenceFrameAnimation::setAnimationSequence(AnimationSequence sequence)
{
bool ret = (BeginToEnd == sequence) || (EndToBegin == sequence);
if(ret)
{
m_animationSequence = sequence;
}
return ret;
}
bool SequenceFrameAnimation::setStartFrameIndex(int index)
{
bool ret = (0 <= index) && (index <= (m_row * m_col));
if(ret)
{
m_frameIndex = index;
int x = 0;
int y = 0;
if(BeginToEnd == m_animationSequence)
{
x = (m_frameIndex % m_col) * m_perFrameWidth;
y = (m_frameIndex / m_col) * m_perFrameHeight;
m_currentFrameRect = QRect(x, y, m_perFrameWidth, m_perFrameHeight);
}
else
{
x = m_sequenceFramePixmap.width() - (m_frameIndex % m_col + 1) * m_perFrameWidth;
y = m_sequenceFramePixmap.height() - (m_frameIndex / m_col + 1) * m_perFrameHeight;
m_currentFrameRect = QRect(x, y, m_perFrameWidth, m_perFrameHeight);
}
update();
}
return ret;
}
void SequenceFrameAnimation::setScaled(const QSize& size)
{
m_scaledSize = size;
}
void SequenceFrameAnimation::setLoopEnabled(bool enabled)
{
m_loop = enabled;
}
void SequenceFrameAnimation::setSpeedUpEnabled(bool enabled)
{
m_needSpeedUp = enabled;
}
bool SequenceFrameAnimation::start()
{
bool ret = m_sequenceFramePixmap.isNull() ? false : true;
if(ret)
{
m_sequenceFramePixmap = m_sequenceFramePixmap.scaled(m_scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
m_perFrameWidth = m_sequenceFramePixmap.width() / m_col;
m_perFrameHeight = m_sequenceFramePixmap.height() / m_row;
int x = 0;
int y = 0;
if(BeginToEnd == m_animationSequence)
{
x = (m_frameIndex % m_col) * m_perFrameWidth;
y = (m_frameIndex / m_col) * m_perFrameHeight;
m_currentFrameRect = QRect(x, y, m_perFrameWidth, m_perFrameHeight);
}
else
{
x = m_sequenceFramePixmap.width() - (m_frameIndex % m_col + 1) * m_perFrameWidth;
y = m_sequenceFramePixmap.height() - (m_frameIndex / m_col + 1) * m_perFrameHeight;
m_currentFrameRect = QRect(x, y, m_perFrameWidth, m_perFrameHeight);
}
resize(m_perFrameWidth, m_perFrameHeight);
if(m_needSpeedUp)
{
m_currentSpeedUpCount = m_maxSpeedUpCount - 1;
int time = getCurrentSpeedUpTime();
m_speedUpTimer.setInterval(time);
m_speedUpTimer.start();
}
else
{
m_speedUpTimer.start(m_frameRate);
}
update();
}
return ret;
}
void SequenceFrameAnimation::stop()
{
m_timer.stop();
}
void SequenceFrameAnimation::onTimeout()
{
m_frameIndex++;
if(m_frameIndex >= (m_row * m_col))
{
if(m_loop)
{
m_frameIndex = 0;
}
else
{
m_timer.stop();
m_frameIndex = 0;
return;
}
}
int x = 0;
int y = 0;
if(BeginToEnd == m_animationSequence)
{
x = (m_frameIndex % m_col) * m_perFrameWidth;
y = (m_frameIndex / m_col) * m_perFrameHeight;
}
else
{
x = m_sequenceFramePixmap.width() - (m_frameIndex % m_col + 1) * m_perFrameWidth;
y = m_sequenceFramePixmap.height() - (m_frameIndex / m_col + 1) * m_perFrameHeight;
}
m_currentFrameRect = QRect(x, y, m_perFrameWidth, m_perFrameHeight);
update();
}
void SequenceFrameAnimation::onSpeedUp()
{
m_currentSpeedUpCount--;
m_frameIndex++;
m_frameIndex = m_frameIndex % (m_row * m_col);
if(m_currentSpeedUpCount <= 0)
{
m_speedUpTimer.stop();
m_timer.start(m_frameRate);
m_currentSpeedUpCount = m_maxSpeedUpCount;
}
else
{
int x = 0;
int y = 0;
if(BeginToEnd == m_animationSequence)
{
x = (m_frameIndex % m_col) * m_perFrameWidth;
y = (m_frameIndex / m_col) * m_perFrameHeight;
}
else
{
x = m_sequenceFramePixmap.width() - (m_frameIndex % m_col + 1) * m_perFrameWidth;
y = m_sequenceFramePixmap.height() - (m_frameIndex / m_col + 1) * m_perFrameHeight;
}
m_currentFrameRect = QRect(x, y, m_perFrameWidth, m_perFrameHeight);
int time = getCurrentSpeedUpTime();
m_speedUpTimer.setInterval(time);
update();
}
}
void SequenceFrameAnimation::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
painter.drawPixmap(rect(), m_sequenceFramePixmap, m_currentFrameRect);
}
int SequenceFrameAnimation::getCurrentSpeedUpTime()
{
return (m_frameRate + m_currentSpeedUpCount * SPEEDUP_INTERVAL);
}
四、使用示例
#include <QApplication>
#include "groupsucceedwidget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
SequenceFrameAnimation w1;
w1.setSequenceFrameAnimation(":/images/voiceSequence.png", 243, 36, 200);
w1.setAnimationSequence(SequenceFrameAnimation::BeginToEnd); //播放顺序:头到尾
w1.setLoopEnabled(true); //循环播放
w1.show();
w1.start();
return a.exec();
}
序列帧图: