前言

目前见的比较多的轮播图有平移和旋转两种方式。平移类似淘宝那种切换幻灯片一样的效果,旋转一般是近大远小,看起来有点3D的感觉。本文代码实现旋转轮播图效果如下:

完整代码链接: https://github.com/gongjianbo/MyTestCode/tree/master/Qt/TestQt_20211029_Swiper

设计思路思路

1.计算坐标

可以现在 3D 场景中设计效果,图片是绕着 Y 轴旋转的,如下图:

由于我们的观察点是固定的,就能拆分成两个平面进行处理:

一个是 xz 组成的旋转轨迹平面,x 对应绘制时的坐标点 x 分量,z 对应绘制时的前后顺序,近大远小,同时 z 值更大的堆叠在上层。可以根据球圆上点坐标的方式根据旋转角度计算 x 和 z 的值。

另一个是绘制时的 xy 屏幕坐标系,可以直接将 y 在图上居中,x 取上一步获取到的 x 值。要注意的是 Qt 绘制时,原点在左上角,右下角为正方向。

2.切换动画

切换动画可以用 QPropertyAnimation 属性动画来做。

要注意的一个点就是头尾切换时。如果第一个切换到最后一个,可以将当前 index 设置为最后一个 index+1,这样再去减 1 动画就能正常过渡;或者最后一个切换到第一个,可以将当前 index 设置为第一个 index-1,这样再去加 1 动画就能正常过渡。否则可能出现从头逐个切换到最后或者反过来,这明显就破坏了旋转的连续性。

主要实现代码

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QPainterPath>
#include <QMouseEvent>
#include <QHoverEvent>
#include <QPainter>
#include <QImage>
#include <QPropertyAnimation>
#include <QTimer>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTQ_PROPERTY(double curIndex READ getCurIndex WRITE setCurIndex NOTIFY curIndexChanged)
public:MainWindow(QWidget *parent = nullptr);~MainWindow();double getCurIndex() const;void setCurIndex(double index);protected:bool event(QEvent *e) override;void paintEvent(QPaintEvent *event) override;void resizeEvent(QResizeEvent *event) override;private://计算图片位置void calcImagePos();//计算按钮位置void calcBtnPath();//切换到下一个图void toPrev();//切换到上一个图void toNext();signals:void curIndexChanged();private:Ui::MainWindow *ui;struct ImageNode{QImage img;double xf{0};double yf{0};double zf{0};};//当前图片列表QVector<ImageNode> imageList;//按钮位置QVector<QRectF> btnList;//图片z堆叠顺序QVector<int> drawList;//当前设置的indexint setIndex{0};//属性动画绘制用到的indexdouble curIndex{0.0};//旋转动画QPropertyAnimation animation;//自动切换定时器QTimer swipTimer;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"#include <cmath>
#include <QtMath>
#include <QDebug>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);setMouseTracking(true);//界面上放了两个按钮用来前进后退,切换当前图片connect(ui->btnPrev,&QPushButton::clicked,this,&MainWindow::toPrev);connect(ui->btnNext,&QPushButton::clicked,this,&MainWindow::toNext);//随便搞五个色块image当作图片int alpha=255;QList<QColor> colors=QList<QColor>{QColor(255,0,0,alpha),QColor(0,255,0,alpha),QColor(0,0,255,alpha),QColor(255,255,0,alpha),QColor(0,255,255,alpha) };for(int i=0;i<colors.size();i++){ImageNode node;node.img=QImage(200,120,QImage::Format_ARGB32);node.img.fill(colors.at(i));QPainter p(&node.img);p.fillRect(QRectF(10,10,20,20),Qt::gray);imageList.append(node);}calcImagePos();calcBtnPath();//自动切换connect(&swipTimer,&QTimer::timeout,[this]{//这里可以判断下是否hover某个图,不切换,略toNext();});swipTimer.start(2000);//切换动画animation.setTargetObject(this);animation.setPropertyName("curIndex");animation.setEasingCurve(QEasingCurve::OutQuart);animation.setDuration(1000);//动画结束后定时切换connect(&animation,&QPropertyAnimation::stateChanged,this,[this](QAbstractAnimation::State newState, QAbstractAnimation::State oldState){oldState;if(newState!=QAbstractAnimation::Stopped){swipTimer.stop();}else{swipTimer.start(2000);}});
}MainWindow::~MainWindow()
{//避免结束程序stop时异常swipTimer.disconnect(this);animation.disconnect(this);swipTimer.stop();animation.stop();delete ui;
}double MainWindow::getCurIndex() const
{return curIndex;
}void MainWindow::setCurIndex(double index)
{curIndex=index;emit curIndexChanged();//属性动画设置值会调用该接口,此处计算位置并刷新calcImagePos();update();
}bool MainWindow::event(QEvent *e)
{switch (e->type()) {case QEvent::HoverMove:{QHoverEvent *hover = static_cast<QHoverEvent*>(e);if(hover){//放到哪个按钮上就切换到对应图片for(int i=0;i<btnList.size();i++){if(btnList.at(i).contains(hover->pos())){setIndex=i;animation.stop();animation.setStartValue(getCurIndex());animation.setEndValue((double)setIndex);animation.start();break;}}}}break;default:break;}return QMainWindow::event(e);
}void MainWindow::paintEvent(QPaintEvent *event)
{Q_UNUSED(event)QPainter painter(this);painter.fillRect(rect(),Qt::white);if(imageList.isEmpty())return;//切换两种效果if(!ui->checkBox->isChecked()){painter.save();//平移到中心点绘制,便于计算painter.translate(width()/2,height()/2);for(int i=0;i<imageList.size()&&i<drawList.size();i++){const ImageNode &node=imageList.at(drawList.at(i));QPointF center=QPointF(node.xf*node.img.width(),node.zf*30);//缩放系数归一化到[0.5,1.0]const double scale=0.5+0.25*(node.zf+1);QRectF rect=QRectF(0,0,node.img.width()*scale,node.img.height()*scale);rect.moveCenter(center);painter.drawImage(rect,node.img);}painter.restore();}else{for(int i=0;i<imageList.size()&&i<drawList.size();i++){const ImageNode &node=imageList.at(drawList.at(i));QPointF center=QPointF(node.xf*node.img.width(),node.zf*20);//缩放系数归一化到[0.5,1.0]const double scale=0.5+0.25*(node.zf+1);QRectF rect=QRectF(0,0,node.img.width()*scale,node.img.height()*scale);rect.moveCenter(rect.topLeft());painter.save();painter.translate(width()/2,height()/2);painter.translate(center);QTransform trans;const double step=360.0/imageList.size();const double degree=step*drawList.at(i)-curIndex*step;trans.rotate(-degree,Qt::YAxis);painter.setTransform(trans, true);painter.drawImage(rect,node.img);painter.restore();}}//底部画几个圆圈painter.setPen(Qt::NoPen);painter.setRenderHint(QPainter::Antialiasing);for(int i=0;i<btnList.size();i++){painter.setBrush(i==setIndex?Qt::black:Qt::gray);painter.drawEllipse(btnList.at(i));}
}void MainWindow::resizeEvent(QResizeEvent *event)
{QMainWindow::resizeEvent(event);calcBtnPath();
}void MainWindow::calcImagePos()
{if(imageList.isEmpty())return;drawList.resize(imageList.size());//每个图之间的角度间隔const double step=360.0/imageList.size();//绘制时会平移中心点,所以这里以0.0为中心点计算for(int i=0;i<imageList.size();i++){ImageNode &node=imageList[i];//0度为0,+90度是让当前图z值为1const double degree=90+step*i-curIndex*step;const double radians=qDegreesToRadians(degree);//取反则为顺时针变化node.xf=-cos(radians);node.zf=sin(radians);//存下标用于计算堆叠顺序drawList[i]=i;}//根据z排堆叠顺序std::sort(drawList.begin(),drawList.end(),[this](int a, int b){return imageList.at(a).zf<imageList.at(b).zf;});
}void MainWindow::calcBtnPath()
{if(imageList.isEmpty())return;//按钮也可以根据curIndex值来做过渡动画,略btnList.resize(imageList.size());//绘制位置的规则自己随便定int w=rect().width();int h=rect().height();//底部画几个圆圈int btn_space=20;int btn_width=14;int bar_width=btn_width*imageList.size()+btn_space*(imageList.size()-1);for(int i=0;i<imageList.size();i++){btnList[i]=QRectF(w/2-bar_width/2+(btn_space+btn_width)*i,h/2+100,btn_width,btn_width);}
}void MainWindow::toPrev()
{animation.stop();setIndex--;//到头了,就切换到尾巴上,为了动画连续,startvalue也设置到尾巴上if(setIndex<0){setIndex=imageList.size()-1;animation.setStartValue(getCurIndex()+imageList.size());}else{animation.setStartValue(getCurIndex());}animation.setEndValue((double)setIndex);animation.start();
}void MainWindow::toNext()
{animation.stop();setIndex++;//到尾了,就切换到头上,为了动画连续,startvalue也设置到头上if(setIndex>imageList.size()-1){setIndex=0;animation.setStartValue(getCurIndex()-imageList.size());}else{animation.setStartValue(getCurIndex());}animation.setEndValue((double)setIndex);animation.start();
}

Qt绘制旋转的轮播图相关推荐

  1. 我给pyecharts绘制的“时间轮播图”,加上了好玩儿的“图形标志”!

    本文说明 这里我需要事先说明一点:不管学习什么,官网是最好的老师.对于pyecharts绘图库,知道了他的绘图原理后,不管绘制任何图形难度并不是很大,唯一难住我们的就是pyecharts的参数太多,我 ...

  2. css简单样式(旋转正方形、纸片旋转、轮播图3D、简单轮播图)

    旋转正方形 <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" c ...

  3. 好玩!PyEcharts 绘制时间轮播图

    作者 | 黄伟呢 来源 | 数据分析与统计学之美 这里我需要事先说明一点:不管学习什么,官网是最好的老师.对于pyecharts绘图库,知道了它的绘图原理后,不管绘制任何图形难度并不是很大,唯一难住我 ...

  4. 关于图片轮播图的一个简单实例 以及实例中发现问题

    1.最近在学习JS的过程中,为了巩固水平做了一个简单的轮播图,以及在做的过程中发现一些问题(未解决!希望可以有大佬可以解释这个问题) 2.代码如下: <!DOCTYPE html> < ...

  5. Axure绘制轮播图

    相信大家在日常的原型绘制中经常会进行轮播图的设计,轮播图对于产品的重要性,在这里就不给大家做过多的强调了,本文主要是教大家如何使用动态面板制作轮播图,废话不多说,直接上干货 一.效果展示 二.功能分析 ...

  6. 【python教程入门学习】用pyecharts绘制带动画效果的“时间轮播图

    今天我们要分享的是一个数据可视化的案例. 在讲述本文之前,为了满足大家的好奇心,我们先来看看最终做出来的效果呈现. 不管学习什么技术,官网是最好的老师.对于pyecharts绘图库,知道了他的绘图原理 ...

  7. 技术解析:如何用pyecharts绘制时间轮播图

    在前天的文章『用python制作动态图表看全球疫情变化趋势』中,由于篇幅原因,在数据处理与数据可视化相关内容上我们只是简单带过,那么我将以python小小白的角度去还原如何处理数据与数据可视化.本文为 ...

  8. 三、bootstrap4 组件(警告和提示框、徽章和面包屑、按钮按钮组、卡片、列表组、导航和选项卡、分页和进度条、巨幕和旋转图标、轮播图、折叠菜单、下拉菜单、导航条、滚动监听、轻量弹框、模态框、表单)

    1.1 警告提示框 1.2 徽章和面包屑 1.3 按钮和按钮组 1.4 卡片 1.5 列表组 1.6 导航和选项卡 1.7 分页和进度条 1.8 巨幕和旋转图标 1.9 轮播图 1.10 折叠菜单 1 ...

  9. qt 动画 顺序 轮播图轮播图

    Show time! 简单来说,就是一个轮播图. 切换的时候是有动画的. 点击下面的按钮可以切换动画. 图片可以是很多张很多张的,但显示在窗口上的只有三张,但它们的顺序是不会变的. 如果能直接有qml ...

最新文章

  1. 教你如何使用Solitude评估应用程序中的用户隐私问题
  2. 两个数从大到小排列输出
  3. 光流 | 特征光流之视频中物体检测一(论文分享)
  4. 九度 1462:两船载物问题(01背包)
  5. 雪妖现世:给SAP Fiori Launchpad增添雪花纷飞的效果
  6. NET问答: 多个 await 和 Task.WaitAll 是等价的吗?
  7. Visual Studio 2017的第五个更新包扩展了调试工具
  8. linux权限源码分析,Linux基础之文件权限详解
  9. 运维组如何管理服务器资源,运维服务管理体系方案全套.doc
  10. 自定义php报错信息,自定义PHP的错误报告处理方式
  11. 自动驾驶——多目标跟踪模块的学习笔记
  12. 【VUE】vue在vue-cli3环境下基于axios解决跨域问题
  13. Python数据可视化大屏最全教程(全)
  14. 针式打印机的使用与维护
  15. PHP工程师的最佳学习路线
  16. css:字母hover文字加粗,盒子变宽,导致文字列表抖动
  17. 《Delphi传奇》网络游戏组件安装步骤:Delphi 10.3安装DelphiX
  18. bash xx.sh与sh xx.sh以及./xx.sh的区别
  19. bmp文件c语言压缩算法,BMP文件数据压缩与解压缩方法.pdf
  20. 生活中的ps!女朋友把菜花烤了一下,结果......

热门文章

  1. 【arduino】arduino家族,arduino相关各种开发环境汇总,Mixly米思齐最新python开发环境...
  2. sudo unable to resolve host test : Name or service not known
  3. 手机数字雨_cmd命令如何实现数字雨的效果
  4. MATLAB图像基本操作(信息查询/文件读取/写入/显示)
  5. 求助:matlab报错:位置 2 处的索引超出数组边界(1)
  6. 什么是类。什么是对象
  7. SpringBoot项目打包成jar后读取包内文件报错问题解决方法
  8. 2范数和F范数的区别
  9. NMS(Non-Maximum Suppression,非极大值抑制)解析
  10. Linux tty串口测试程序