效果如下:
实现思路:
1、放了1个stacked widget
2、里面放了2个widget,一个蓝色一个红色
3、鼠标点击时,将2个widget隐藏,开启动画
4、通过动画不停的修改旋转角度,然后重绘界面
5、重绘时:将两个widget的内容渲染到2个QPixmap中,然后将这2个pixmap绕y轴旋转绘制上去
小于90度时绘制第一个pixmap,大于90时绘制第二个pixmap
6、动画结束后,设置stacked widget的当前widget
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QStackedWidget>
#include<QHBoxLayout>
#include<QPainter>
#include<QTransform>
#include<QPropertyAnimation>
#include<QPushButton>
class Widget : public QWidget
{
Q_OBJECT
Q_PROPERTY(int rotation READ getRotation WRITE setRotation NOTIFY rotationChanged FINAL)
public:
Widget(QWidget *parent = nullptr) : QWidget(parent)
{
resize(800,600);
QHBoxLayout* h_box=new QHBoxLayout(this);
//放了一个stacked widget
sw=new QStackedWidget(this);
h_box->addWidget(sw);
//往里面放两个Widget 一个红色一个蓝色,点击后实现翻转效果切换
w1=new QWidget(this);
w1->setStyleSheet("background-color:red");
QPushButton* btn=new QPushButton("我是w1",w1);
btn->setGeometry(20,20,100,30);
sw->addWidget(w1);
w2=new QWidget(this);
w2->setStyleSheet("background-color:blue");
QPushButton* btn2=new QPushButton("我是w2",w2);
btn2->setGeometry(20,20,100,30);
sw->addWidget(w2);
sw->setCurrentIndex(0);
//使用QPropertyAnimation来实现动画,不停的修改旋转角度rotation
animation=new QPropertyAnimation(this,"rotation",this);
animation->setStartValue(0);
animation->setEndValue(180);
animation->setDuration(2000);//设置动画时长2s
//动画结束后将对应stacked widget要显示的widget更新并显示
connect(animation,&QPropertyAnimation::finished,this,[=](){
//更新stacked widget里面当前的widget
int index=sw->currentIndex();
if(index==sw->count()-1)
{
index=0;
}
else{
++index;
}
sw->setCurrentIndex(index);
});
}
~Widget()=default;
int getRotation() const
{
return rotation;
}
//旋转角度每次更新时都update重绘
void setRotation(int newRotation)
{
if (rotation == newRotation)
return;
rotation = newRotation;
emit rotationChanged();
update();
}
signals:
void rotationChanged();
protected:
//主要思路,将w1和w2先隐藏,
//然后旋转角度小于90度时将w1的内容渲染到一个QPixmap上,旋转角度大于90度时将w2的内容渲染到QPixmap上
void paintEvent(QPaintEvent* ev) override
{
//只有动画开启后才执行这一段的绘制
if(animation->state()==QPropertyAnimation::Running)
{
QPainter painter(this);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
QPixmap pix(sw->size());
//使用变换类QTransform搭配QPainter
QTransform trans;
trans.translate(rect().center().x(),rect().center().y());//移动到中心
//小于90度
if(rotation<90)
{
//将stacked widget中当前widget渲染到图片上
sw->currentWidget()->render(&pix);
trans.rotate(rotation,Qt::YAxis);//QTransform的旋转可以设置绕某个轴旋转,这里绕y轴旋转
}
else//大于90度
{
//将下一个widget渲染到图片上
int index=sw->currentIndex();
if(index==sw->count()-1)
{
index=0;
}
else{
++index;
}
sw->widget(index)->render(&pix);
//解决镜像问题180-rotation
trans.rotate(rotation-180,Qt::YAxis);
}
painter.setTransform(trans);
//将被渲染了w1或w2内容的QPixmap绘制上去
painter.drawPixmap(QPoint(-sw->width()/2,-sw->height()/2),pix);
}
QWidget::paintEvent(ev);
}
//鼠标点击时开启动画效果
void mousePressEvent(QMouseEvent* ev)
{
//动画还在执行中,那么等他执行完
if(animation->state()==QPropertyAnimation::Running)
{
return;
}
//先将stacked widget中的2个widget隐藏
for(int i=0;i<sw->count();++i)
{
//解决第一次翻转时,其中有些widget的尺寸和stacked widget不一致导致渲染搭配pixmap上的内容太小
sw->widget(i)->resize(sw->size());
sw->widget(i)->hide();
}
animation->start();
}
private:
QStackedWidget* sw;
QWidget* w1;
QWidget* w2;
int rotation=0;
QPropertyAnimation* animation;
};
#endif // WIDGET_H
知识点:
- 可以将widget中的内容渲染到一张图片中QPixmap
QWidget::render方法
QPixmap pixmap(widget->size());
widget->render(&pixmap);
- 利用QTransform给QPainter设置变换
其中rotate方法可以设置使得QPainter绕某个轴(x,y,z)旋转
默认是z轴,也就是在xy平面中旋转
QTransform &QTransform::rotate(qreal a, Qt::Axis axis = Qt::ZAxis)
QTransform trans;
trans.translate(rect().center().x(),rect().center().y());//移动到中心
trans.rotate(rotation,Qt::YAxis);//QTransform的旋转可以设置绕某个轴旋转,这里绕y轴旋转
painter.setTransform(trans);