使用QPropertyAnimation类结合QPainterPath类实现路径动画效果
效果图
头文件
#ifndef QWHANIMATIONPATH_H
#define QWHANIMATIONPATH_H
#include <QPainterPath>
#include <QEasingCurve>
class QGraphicsScene;
class QGraphicsProxyWidget;
class QPropertyAnimation;
class QWidget;
class QWHAnimationPath : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal m_curPercent READ getCurPercent WRITE setCurPercent)
public:
enum Type
{
COMMON, // 常规
SLOPE, // 斜率
};
QWHAnimationPath(QGraphicsScene *scene);
~QWHAnimationPath();
// 设置路径
void setPath(const QPainterPath &path);
// 设置路径窗口
void setWidget(QWidget *widget, QGraphicsProxyWidget *proxyWidget);
// 设置路径动画类型
void setType(Type type);
// 设置路径位置初始百分比
void setStartPercent(qreal startPercent);
// 设置路径位置结束百分比
void setEndPercent(qreal endPercent);
// 设置路径动画时长
void setDuration(int msecs);
// 设置路径动画效果过度曲线
void setEasingCurve(const QEasingCurve &curve);
// 开启动画
void start();
// 暂停动画
void pause();
// 结束动画
void stop();
private:
// 设置路径位置百分比
void setCurPercent(qreal percent);
// 获取路径位置百分比
qreal getCurPercent();
// 获取曲线斜率
qreal angle(const QPointF &p1, const QPointF &p2);
private:
qreal m_startPercent; // 路径位置初始百分比
qreal m_endPercent; // 路径位置结束百分比
qreal m_curPercent; // 路径位置当前百分比
int m_duration; // 路径动画时长
QEasingCurve m_easingCurve; // 路径动画效果过度曲线
QGraphicsScene *m_scene; // 场景
QWidget *m_widget; // 路径窗口
QGraphicsProxyWidget *m_proxyWidget; // 路径代理窗口
QPainterPath m_path; // 路径
QPropertyAnimation *m_animation; // 属性动画
Type m_type; // 动画类型
};
#endif // QWHANIMATIONPATH_H
核心代码
void QWHAnimationPath::setCurPercent(qreal percent)
{
if (m_widget)
{
qreal m_lastPercent = m_curPercent;
m_curPercent = percent;
QPointF pos = m_path.pointAtPercent(percent);
if (COMMON == m_type)
m_proxyWidget->setPos(pos);
else {// SLOPE == m_type
QPointF p1 = m_path.pointAtPercent(m_lastPercent);
QPointF p2 = m_path.pointAtPercent(m_curPercent);
qreal angle = this->angle(p1, p2);
m_proxyWidget->setPos(pos);
m_proxyWidget->setRotation(angle + 90);
}
}
}
qreal QWHAnimationPath::angle(const QPointF &p1, const QPointF &p2)
{
if (abs(p1.x() - p2.x()) < 0.000001)
return ((p1.y() < p2.y()) ? 90 : -90);
if (abs(p1.y() - p2.y()) < 0.000001)
return ((p1.x() < p2.x()) ? 0 : 180);
qreal angle = 0.0;
if (p1.x() > p2.x())
angle = qAtan((p2.y() - p1.y()) / (p2.x() - p1.x())) * 180.0 / M_PI;
else
angle = qAtan((p2.y() - p1.y()) / (p2.x() - p1.x())) * 180.0 / M_PI + 180;
return angle;
}
示例代码
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
// 视图场景设置
m_scene = new QGraphicsScene(-800, -450, 1600, 900);
ui->graphicsView->setScene(m_scene);
ui->graphicsView->setBackgroundBrush(QColor(213, 240, 247));
ui->graphicsView->setRenderHint(QPainter::Antialiasing);
// 成员初始化
m_commonPath = new QWHAnimationPath(m_scene);
m_slopePath = new QWHAnimationPath(m_scene);
m_slopePathLove = new QWHAnimationPath(m_scene);
m_btn = new QPushButton("路径窗口");
QGraphicsProxyWidget *proxyWidget = m_scene->addWidget(m_btn);
proxyWidget->setZValue(100);
// 图元设置
m_circleItem = m_scene->addEllipse(-150, -150, 300, 300);
m_rectItem = m_scene->addRect(-800, -150, 400, 300);
QPainterPath pathLove;
pathLove.moveTo(800, -100);
pathLove.arcTo(600, -200, 200, 200, 0, 180);
pathLove.arcTo(400, -200, 200, 200, 0, 180);
pathLove.lineTo(600, 200);
pathLove.lineTo(800, -100);
m_pathItem = m_scene->addPath(pathLove, QPen(Qt::red), QBrush(QColor(255, 191, 213)));
// 普通路径
QPainterPath path;
QPolygonF polygonF = m_rectItem->mapToScene(m_rectItem->rect());
path.addPolygon(polygonF);
m_commonPath->setPath(path);
m_commonPath->setWidget(m_btn, proxyWidget);
m_commonPath->setStartPercent(0.0);
m_commonPath->setEndPercent(1.0);
m_commonPath->setDuration(5000);
// 斜率路径
QPainterPath path2;
path2.addEllipse(-150, -150, 300, 300);
m_slopePath->setPath(path2);
m_slopePath->setWidget(m_btn, proxyWidget);
m_slopePath->setType(QWHAnimationPath::SLOPE);
m_slopePath->setStartPercent(0.0);
m_slopePath->setEndPercent(1.0);
m_slopePath->setDuration(5000);
// 斜率路径Love
m_slopePathLove->setPath(pathLove.toReversed());
m_slopePathLove->setWidget(m_btn, proxyWidget);
m_slopePathLove->setType(QWHAnimationPath::SLOPE);
m_slopePathLove->setStartPercent(0.0);
m_slopePathLove->setEndPercent(1.0);
m_slopePathLove->setDuration(5000);
}