2020年11月24日晚上焦灼的我,啊啊啊啊,最近类的一批,放个图解压一下,如果觉得不错的我可以更新以下这个是怎么绘制的,暂时仅仅用来玩,数据随机生成的。
自控,永远滴神!呜呜呜
这是程序半成品的源码,可以下载玩玩:
链接:https://pan.baidu.com/s/1sNlB59R3pZAVCquDrAnYJw
提取码:by3b
复制这段内容后打开百度网盘手机App,操作更方便哦
///
2020年11月25日考完自控后五味杂陈的我,正经代码说明:
我感觉这个最难的肯定是那个余晖效果怎么做对吧,其实用的是gdi+的渐变画刷,渐变画刷有四个参数,我来给大家说明一下。为了便于说明,我就直接在代码里面说明了。
//这里很简单,创建一个graphic对象,通过指定控件的句柄
Graphics gGraphics(form1.Control(ID_picMain).hWnd());//这里我这种写法用的是我们老师的一个通用模块,可以获得对应ID名称的控件的句柄
RECT rc;
GetWindowRect(form1.Control(ID_picMain).hWnd(),&rc);
Point center(int(0.5 * 300), int(0.5 * 300)); //求得并保存这个控件的中间位置,便于移动坐标,具体使用后面在说
int i;
const int iDiff = 18; //表示的是绘制的雷达图的圆的间隔
//这里用的是双缓冲的方式绘制的,所以新创建一个画布,首先在这个上面绘制内容,然后拷贝到对应控件的对象上面显示
Bitmap bmpBuffer(300, 300); //大小300*300的画布
//通过画布创建一个graphic对象,后面用于画图,这里很简单
Graphics buffer(&bmpBuffer);
//全黑背景,首先创建画刷
m_gdiPlus.GetBrush()->SetColor(Color::Black); //这句代码,你可以自己创建,我这个是自己写的一个类,所以写法不太一样
/*自己创建画刷的代码:(后面的画笔什么的自己写吧,我不多写注释介绍怎么创建了)
SolidBrush sBrush(Color::Black);
*/
buffer.FillRectangle(m_gdiPlus, 0, 0, rc.right - rc.left, rc.bottom - rc.top); //填充矩形区域为黑色
//至于坐标面的东西,看代码下面的解释,此处不解释了
//一定注意,下面坐标都是在转换之后的坐标系绘制的
buffer.ResetTransform(); //重置坐标面
buffer.TranslateTransform((REAL)center.X, (REAL)center.Y); //平移变换,将坐标原点平移至客户区中心
buffer.RotateTransform(m_fAngel);
//绘制基础的坐标圆,绘制10个圈圈表示距离
m_gdiPlus.GetPen()->SetColor(Color::Green);
m_gdiPlus.GetPen()->SetWidth(2);
for (i=0;i<10;i++)
{
buffer.DrawEllipse(m_gdiPlus, -i * iDiff, -i * iDiff, 2*i * iDiff, 2*i * iDiff);
}
//渐变画刷的使用,具体介绍看代码下面的说明
//绘制指针的 pie
buffer.RotateTransform(-60);
LinearGradientBrush linGrBrush(Point(0, 150), Point(0, 0), Color(255, 0, 255, 0), Color(0, 0, 100, 0));//渐变画刷
buffer.FillPie(&linGrBrush, -9*iDiff, -9 * iDiff, 2*9 * iDiff, 2*9 * iDiff, 0, 60); //绘制渐变扇形
buffer.ResetTransform(); //重置坐标面
buffer.TranslateTransform((REAL)center.X, (REAL)center.Y); //平移变换,将坐标原点平移至客户区中心
//
//绘制对应的直线坐标内容和提示方向文本
//buffer.ResetTransform(); //重置坐标面
buffer.DrawLine(m_gdiPlus, -9 * iDiff, 0, 9 * iDiff, 0);
buffer.DrawLine(m_gdiPlus, 0, -9 * iDiff, 0, 9 * iDiff);
m_gdiPlus.GetBrush()->SetColor(Color::Green);
PointF mpoint0(0, -8 * iDiff);
buffer.DrawString(TEXT("N"), -1, m_gdiPlus, mpoint0, (Brush*)m_gdiPlus);
PointF mpoint1(-8 * iDiff, 0);
buffer.DrawString(TEXT("W"), -1, m_gdiPlus, mpoint1, (Brush*)m_gdiPlus);
PointF mpoint2(8 * iDiff, 0);
buffer.DrawString(TEXT("E"), -1, m_gdiPlus, mpoint2, (Brush*)m_gdiPlus);
PointF mpoint3(0, 8 * iDiff);
buffer.DrawString(TEXT("S"), -1, m_gdiPlus, mpoint3, (Brush*)m_gdiPlus);
//绘制点实现方式下面说明:
//绘制对应的点的内容
static bool fa = true;
for (i=0;i<10;i++)
{
if (m_RadaData[i].iFrameNum != -1)
{
m_gdiPlus.GetBrush()->SetColor(Color((50-m_RadaData[i].iFrameNum) / 50.0 * 250, 0, 255, 0));
//求得对应点的位置,然后以该点为中心,绘制一个小点代表目标
buffer.FillEllipse(m_gdiPlus,
m_RadaData[i].iLen * cos(m_RadaData[i].fAngle / 180 * 3.14) - 6,
m_RadaData[i].iLen * sin(m_RadaData[i].fAngle / 180 * 3.14) - 6,
12, 12);
//此处主要是为了延迟它的变化频率变慢一点,否则点消失的太快效果不好
fa = !fa;
if (fa) m_RadaData[i].iFrameNum++;
if (m_RadaData[i].iFrameNum >= 50)
{
m_RadaData[i].iFrameNum = -1;
m_RadaData[i].fAngle = rand() % 360;
m_RadaData[i].iLen = rand() % 100 + 50;
}
}
}
//这里就是其他操作了,可有可无的
buffer.ResetTransform(); //重置坐标面
buffer.SetCompositingQuality(CompositingQualityHighQuality);
//双缓冲的内容重新绘制到控件上面
m_gdiPlus.GetGraphics()->DrawImage(&bmpBuffer, 100, 0, 300, 300);
1.平移变换
buffer.TranslateTransform((REAL)center.X, (REAL)center.Y);
2.旋转变换
buffer.RotateTransform(m_fAngel); //角度制,180°这么类似的用
为了更好理解直接放图了
额,应该挺好理解的,不多说。
LinearGradientBrush linGrBrush(
Point(0, 150), Point(0, 0),
Color(255, 0, 255, 0), Color(0, 0, 100, 0));//渐变画刷
其中Color(255,0,0,0);第一个东西是A代表的是透明度,如果A为0,代表刷子刷上去的东西就相当于是只有0被刷上去了,所以就跟没刷上去一样,255是完全不透明。实现方式见下图:
然后刚好可以实现那种波纹的效果是吧,这个图感觉手画的,能够比较清晰的显示内容,有点丑,不要介意,哈哈哈哈哈。(对了,上面不是又转了-60°是吧,因为,我这么绘制是不是你就发现这个扇形前面是从60°出发的,看起来就不太好看,所以又逆时针旋转了60°)
看编程能力,不管会不会,能用结构体枚举绝对不写普通的变量,是吧,结构体一写,一看就是挺有想法的,有经验的,说白了,我觉得唬人可以,哈哈哈哈。不过另外,用结构体便于数据的维护和修改,写多了就知道了。
//结构体用来保存雷达传入的参数的内容,一个是位置信息,一个是对应的显示的帧数信息
struct SRadaData
{
float fAngle; //角度
int iLen; //长度
int iFrameNum = -1; //对应的显示的帧数,为0表示启动,否则表示暂时不可用状态
};
有个定时器,然后会自动不断的刷新雷达旋转的角度,实现动态的效果,这个结构体重要的是第三个参数,我想的是,用数组存储雷达的目标点,然后如果第三个数为-1,代表该点现在不可用,就每次绘制不要显示它,如果发现我扫到了这个点,我就给它置零,表示发现你了,开始绘制这个点吧,这里点渐渐消失是在绘制的时候每次这个数值加上1,这不就可以知道这个点到底是从被发现到现在绘制了几次了,给它对应的透明度,如果超出一定此处就把这个参数设置为-1,表示这个点不可用了,给它一个随机地址,然后重复该过程就实现了该过程。是不是很奇妙呀。
//这里的代码写到定时器的回调函数里面,每次都执行这个内容,定时器如果不太懂的话,另外再说吧,这篇不讲解太多这个东西
m_fAngel+=3; //控制雷达不断的旋转
if (m_fAngel>=360)
{
m_fAngel = 0;
}
//此处判断对应的雷达的角度是否过去了对应的角度的信息
int i;
for (i=0;i<10;i++)
{
if (abs(m_fAngel - m_RadaData[i].fAngle) < 3) //求的是绝对值
{
m_RadaData[i].iFrameNum = 0;
}
}
m_gdiMain.InvalidateAll(); //此处擦除内容,发送WM_PAINT消息让控件重新绘制
暂时写这么多吧,有什么其他的疑问我再更新更新吧,觉得不错的给个赞,/偷瞄,我也知道到底好不好,可以大家一起改正也行,呲牙。