在使用Qt实现动画时,一般使用QPropertyAnimation来实现,一般我们实现控件的移动动画都是走直线,我们如何实现自己想要的移动路径呢,比如走圆弧。下面介绍通过QPropertyAnimation实现自定义动画移动路径,比如走圆曲线、圆弧曲线等
pos动画属性
#include "myanimation.h"
MyAnimation::MyAnimation(QObject *target, const QByteArray &propertyName, QObject *parent):
QPropertyAnimation (target, propertyName, parent)
{
setPathType(LinearPath);
}
void MyAnimation::setPathType(MyAnimation::PathType pathType)
{
if (pathType >= NPathTypes)
qWarning("Unknown pathType %d", pathType);
m_pathType = pathType;
m_path = QPainterPath();
}
void MyAnimation::updateCurrentTime(int currentTime)
{
if (m_pathType == CirclePath || m_pathType == ArcPath) {
if (m_path.isEmpty()) {
QPointF to = endValue().toPointF();
QPointF from = startValue().toPointF();
m_path.moveTo(from);
if(m_pathType == CirclePath) {
m_path.addEllipse(QRectF(from, to));
}
else {
qreal tempWidth = from.rx() - to.rx();
qreal tempHeight = from.ry() - to.ry();
QRectF tempRect = QRectF(to.x() - tempWidth, to.y(), tempWidth*2, tempHeight*2);
m_path.arcTo(tempRect, 0, 90);
}
}
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);
} else {
QPropertyAnimation::updateCurrentTime(currentTime);
}
}
调用
m_pMyAnmation = new MyAnimation(ui->pushButton, "pos", this);
m_pMyAnmation->setDuration(1000);
m_pMyAnmation->setStartValue(QPointF(400, 350));
m_pMyAnmation->setEndValue(QPointF(200, 100));
m_pMyAnmation->setPathType(MyAnimation::ArcPath);
直线动画
圆曲线动画
圆弧曲线
通过重载updateCurrentTime,根据时间节点取到自定义路径的位置,更新updateCurrentValue的值;对于自定义路径,实例中采用QPainterPath绘制圆和圆弧,也可使用QPainterPath绘制直线与圆弧组合等,也可实现其他曲线路径,如sin曲线等。
geometry动画属性
上面例子使用的是pos属性,也可以使用geometry,实现位置与大小组合的动画效果
QRectF startVal = startValue().toRectF();
QRectF endVal = endValue().toRectF();
qreal startWidth = startVal.width();
qreal startHeight = startVal.height();
qreal endWidth = endVal.width();
qreal endHeigth = endVal.height();
if (m_path.isEmpty()) {
QPointF to = endVal.topLeft();
QPointF from = startVal.topLeft();
m_path.moveTo(from);
qreal tempWidth = from.rx() - to.rx();
qreal tempHeight = from.ry() - to.ry();
QRectF tempRect = QRectF(to.x() - tempWidth, to.y(), tempWidth*2, tempHeight);
m_path.lineTo(QPointF(from.rx(), from.ry() - tempHeight/2.));
m_path.arcTo(tempRect, 0, 90);
}
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);
QSizeF size = QSizeF(endWidth + startWidth * (1 - easedProgress), endHeigth + startHeight * (1 - easedProgress));
QRectF retRect = QRectF(pt, size);
updateCurrentValue(retRect);
emit valueChanged(retRect);
调用:
m_pMyAnmation = new MyAnimation(ui->pushButton, "geometry", this);
m_pMyAnmation->setDuration(600);
m_pMyAnmation->setStartValue(QRectF(400, 350, 60, 50));
m_pMyAnmation->setEndValue(QRectF(200, 100, 0, 0));
效果
pos的值采用圆弧曲线的方式,size使用线性的方式,即根据运行百分比获取size的大小
运行的进度会再根据easingCurve来取值,所以可以使用设置相应的缓动曲线,具体效果看该博客https://blog.csdn.net/a137748099/article/details/107292996
m_pMyAnmation->setEasingCurve(QEasingCurve::InQuint);
例子代码下载https://download.csdn.net/download/a137748099/18485759
参考:Qt官方例子easing