今天分享一下我之前一个项目里面设计的一个关于模拟汽车行驶的动画效果类,动画要求不高,所以只做了一些简单的设计,实现了物体在二维平面内按任意方向和速度移动的效果,有图有真相
该类实现的主要功能有(1)初始化加载一张车头朝右的png图片,并且能够实现图片的任意角度旋转和水平垂直翻转;(2)根据汽车行驶路径的折线段将所有顶点以数组形式传入参数,实现车辆按照指定的路线移动;(3)启动内部计时器自动更新位置(或者外部计时器被动更新位置),移动到折线位置时,计算旋转角度旋转图片,移动到终点之后停止更新。见类定义如下:
#ifndef XPDCAR_H
#define XPDCAR_H
#include <QToolButton>
#include <qtimer.h>
class XpdCar : public QToolButton
{
Q_OBJECT
public:
XpdCar(QWidget *parent = NULL);
XpdCar(QImage pix,QSize pixS,QWidget *parent = NULL);
~XpdCar();
void InitData();
QImage filp(const QImage& image,bool bIsHorizon); //按水平轴或者垂直线作镜像翻转,bIsHorizon为true按水平轴,false按垂直方向
QImage rotateImage(const QImage& image,qreal fAngle); //将图片按顺时针方向旋转一定的角度,fAngle为角度值
void setImageRote(qreal r_x,qreal r_y); //根据弧度值(角度值)r_x,r_y确定图片旋转的角度
void setImageRote(int x1,int y1,int x2,int y2); //根据弧度值(角度值)起点(x1,y1)和终点(x2,y2)确定图片旋转的角度
void setData(QVector<QPoint> _v_point,qreal _distance,qreal _speed); //设置行进参数
void setSpeed(qreal _speed); //改变速度
void setPos(int x,int y); //强制设置显示位置
QPoint getPoswithLinedistance(qreal distance,QPoint start,QPoint end); //计算从起点到终点方向距离distance的坐标点
void getCurrentPos(qreal time,int& x,int& y); //根据车速和运动轨迹计算time时间之后位置,timer事件调用move()函数移动到该位置,
//测试_时间触发器
void startTimer(int _msec);
public slots:
void updatedisplay();
private:
QSize l_pixSize;
QImage l_image;
//车辆行进数据结构
QVector<QPoint> v_point; //行驶路径点集合(图上位置)
QVector<qreal> v_linedistance; //行驶路径段在图上的线段长度
qreal distance; //行驶路径总长度(单位m)
qreal linedistance; //行驶路径在图上的总长度
int curposindex; //当前所在点的下标
qreal curlinedistance; //当前所在线段上距离
qreal curlinetotledistance; //当前行驶完成的路径长度总和
int curposx; //当前在图上的点X坐标
int curposy; //当前在图上的点Y坐标
qreal speed; //当前车速
//测试_时间触发器
QTimer timer;
int msec;
};
#endif // XPDCAR_H
类实现的源代码如下:
#include "xpdcar.h"
#include <qmath.h>
#define PI 3.1415926
XpdCar::XpdCar(QWidget *parent)
: QToolButton(parent)
{
l_pixSize = QSize(32,32);
InitData();
}
XpdCar::XpdCar(QImage pix,QSize pixS,QWidget *parent)
: QToolButton(parent),l_image(pix),l_pixSize(pixS)
{
InitData();
}
XpdCar::~XpdCar()
{
}
void XpdCar::InitData()
{
this->setAutoRaise(true);
this->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
this->setStyleSheet("color: rgb(255, 0, 0);\nfont: 75 9pt \"黑体\";");
this->setIconSize(l_pixSize);
this->setIcon(QIcon(QPixmap::fromImage(l_image)));
}
QImage XpdCar::filp(const QImage& image,bool bIsHorizon) //按水平轴或者垂直线作镜像翻转,bIsHorizon为true按垂直方向,false按水平轴
{
return image.mirrored(bIsHorizon,!bIsHorizon);
}
QImage XpdCar::rotateImage(const QImage& image,qreal fAngle) //将图片按顺时针方向旋转一定的角度,fAngle为角度值
{
QMatrix matrix;
matrix.rotate(fAngle);
return image.transformed(matrix,Qt::FastTransformation);
}
void XpdCar::setImageRote(qreal r_x,qreal r_y) //根据弧度值(角度值)r_x,r_y确定图片旋转的角度
{
//坐标和理论坐标轴不同,X轴正向为水平右,Y轴正向为垂直向下
QImage tmp;
if(r_x == 0 && r_y == 0)
return;
if(r_x == 0 ) //Y轴
{
if(r_y > 0)
{
tmp = rotateImage(l_image,90.0);
}
else
{
tmp = rotateImage(l_image,270.0);
}
}
else if(r_y == 0) //X轴
{
if(r_x >0)
{
;
}
else
{
tmp = filp(l_image,true);
}
}
else if(r_x > 0 && r_y > 0) //第一象限
{
qreal k = qAtan(r_y/r_x)*180/PI;
tmp = rotateImage(l_image,k);
}
else if(r_x < 0 && r_y > 0) //第二象限
{
QImage _filp = filp(l_image,true);
qreal k = qAtan(r_y/r_x)*180/PI;
tmp = rotateImage(_filp,360+k);
}else if(r_x < 0 && r_y < 0) //第三象限
{
QImage _filp = filp(l_image,true);
qreal k = qAtan(r_y/r_x)*180/PI;
tmp = rotateImage(_filp,k);
}
else if(r_x > 0 && r_y < 0) //第四象限
{
qreal k = qAtan(r_y/r_x)*180/PI;
tmp = rotateImage(l_image,360.0+k);
}
this->setIcon(QIcon(QPixmap::fromImage(tmp)));
}
void XpdCar::setImageRote(int x1,int y1,int x2,int y2) //根据弧度值(角度值)起点(x1,y1)和终点(x2,y2)确定图片旋转的角度
{
setImageRote(qreal(x2-x1),qreal(y2-y1));
}
QPoint XpdCar::getPoswithLinedistance(qreal distance,QPoint start,QPoint end) //计算从起点到终点方向距离distance的坐标点
{
QPoint p;
qreal t = qSqrt((end.x() - start.x())*(end.x() - start.x()) + (end.y() - start.y())*(end.y() - start.y()));
p.setX((end.x() - start.x())*distance/t + start.x());
p.setY((end.y() - start.y())*distance/t + start.y());
return p;
}
void XpdCar::setData(QVector<QPoint> _v_point,qreal _distance,qreal _speed)//设置行进参数
{
v_point.clear();
v_linedistance.clear();
v_point = _v_point;
distance = _distance;
speed = _speed;
linedistance = 0;
curposindex = -1;
curposx = -1;
curposy = -1;
curlinetotledistance = 0;
for(int i = 1;i<v_point.size();i++)
{
v_linedistance.push_back(qSqrt((v_point[i].x() - v_point[i-1].x())*(v_point[i].x() - v_point[i-1].x())+
(v_point[i].y() - v_point[i-1].y())*(v_point[i].y() - v_point[i-1].y())));
linedistance += v_linedistance.back();
}
if(v_point.size() > 0 )
{
curposindex = 0;
curlinedistance = 0;
curposx = v_point[0].x();
curposy = v_point[0].y();
if(v_point.size() >1)
setImageRote(v_point[0].x(),v_point[0].y(),v_point[1].x(),v_point[1].y());
}
}
void XpdCar::setSpeed(qreal _speed)//改变速度
{
speed = _speed;
}
void XpdCar::setPos(int x,int y)//强制设置显示位置
{
curposx = x;
curposy = y;
}
void XpdCar::getCurrentPos(qreal time,int& x,int& y)//根据车速和运动轨迹计算time时间之后位置,timer事件调用move()函数移动到该位置,
{
if(linedistance <=0 )//图像显示总长度为0
return;
qreal move_line = (time * speed)*linedistance/distance;
if(curposindex == v_point.size()-1)//当前点在终点
{
}
else
{
if(v_linedistance[curposindex] <= curlinedistance + move_line)//超越该点
{
curlinedistance = curlinedistance + move_line - v_linedistance[curposindex];
curposindex++;
if(curposindex == v_point.size()-1)//当前点在终点
{
curlinedistance = 0;
curposx = v_point.back().x();
curposy = v_point.back().y();
}
else
{
QPoint pt = getPoswithLinedistance(curlinedistance,v_point[curposindex],v_point[curposindex+1]);
curposx = pt.x();
curposy = pt.y();
//旋转图标位置
setImageRote(v_point[curposindex].x(),v_point[curposindex].y(),v_point[curposindex+1].x(),v_point[curposindex+1].y());
}
}
else
{
curlinedistance += move_line;
QPoint pt = getPoswithLinedistance(curlinedistance,v_point[curposindex],v_point[curposindex+1]);
curposx = pt.x();
curposy = pt.y();
}
curlinetotledistance += move_line;
}
x = curposx;
y = curposy;
}
void XpdCar::startTimer(int _msec)
{
msec = _msec;
connect(&timer,SIGNAL(timeout()),this,SLOT(updatedisplay()));
timer.start(_msec);
}
void XpdCar::updatedisplay()
{
int x,y;
getCurrentPos(msec*0.001,x,y);
this->move(x,y);
if(curposindex == v_point.size() -1)
timer.stop();
}
我们可以在主函数里面简单测试一下:
#include <QtGui>
#include <QtGui/QApplication>
#include "xpdcar.h"
int main(int argc, char *argv[])
{
QTranslator translator(0);
translator.load(":/XpdCar/Resources/qt_zh_CN.qm");
QApplication a(argc, argv);
a.installTranslator(&translator);
QTextCodec::setCodecForTr(QTextCodec::codecForName("GB2312"));
QTextCodec::setCodecForLocale(QTextCodec::codecForName("GB2312"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GB2312"));
QWidget w;
QImage image(":/XpdCar/Resources/car.png");
XpdCar car(image,image.size(),&w);
QVector<QPoint> _v_point;
_v_point.push_back(QPoint(10,10));
_v_point.push_back(QPoint(200,150));//走第一象限
_v_point.push_back(QPoint(200,350));//走Y轴正向
_v_point.push_back(QPoint(600,500));
_v_point.push_back(QPoint(200,350));//走第三象限
_v_point.push_back(QPoint(400,100));//走第四象限
_v_point.push_back(QPoint(200,350));//走第二象限
car.setData(_v_point,100.0,5.0);
car.startTimer(10);
w.showMaximized();
return a.exec();
}
XpdCar类从QToolButton类继承过来,可以在此基础上添加更多的效果,比如给该类的Text赋值为车牌号,tooltips属性添加司机名称,显示当前车速等等,对了,有一个地方值得注意的是图像区域的坐标系是有点不同的哦,X轴正向是水平朝右,但是Y轴正向是垂直向下的,所以这里的象限定义要多多注意。好了,希望能有更多的程序猿同志多多交流~~