《C# GDI+ 破境之道》:第一境 GDI+基础 —— 第三节:画圆形
有了上一节画矩形的基础,画圆形就不要太轻松+EZ:)所以,本节在画边线及填充上,就不做过多的讲解了,关注一下画“随机椭圆”、“正圆”、“路径填充”的具体实现就好。与画矩形相比较,画椭圆与之完全一致,没有任何特别之处。
在画矩形时,我们使用:
- System.Drawing.Graphics.DrawRectangle(Brush brush, Rectangle rect);
- System.Drawing.Graphics.FillRectangle(Brush brush, Rectangle rect);
在画圆形时,我们使用:
- System.Drawing.Graphics.DrawEllipse(Brush brush, Rectangle rect);
- System.Drawing.Graphics.FillEllipse(Brush brush, Rectangle rect);
看到了吧,就换了个方法名,参数连名都没换:)
所以,了解了如何画矩形,画圆形就是自然掌握的了。
本节就不多费口舌重复了:
1 namespace MikeWare.GdiPlus.Ellipses 2 { 3 using System; 4 using System.Collections.Generic; 5 using System.Drawing; 6 using System.Drawing.Drawing2D; 7 using System.Windows.Forms; 8 9 public partial class FormDrawEllipses : Form 10 { 11 private Random random = null; 12 private Color penColor = Color.Transparent; 13 14 public FormDrawEllipses() 15 { 16 InitializeComponent(); 17 random = new Random(DateTime.Now.Millisecond); 18 penColor = GetRandomColor(); 19 } 20 21 private Point GetRandomPoint() 22 { 23 return new Point(random.Next(0, ClientRectangle.Width), random.Next(0, ClientRectangle.Height - pnlToolbox.Height)); 24 } 25 26 private Rectangle GetRandomRectangle() 27 { 28 var pointA = GetRandomPoint(); 29 var pointB = GetRandomPoint(); 30 31 return new Rectangle(Math.Min(pointA.X, pointB.X) 32 , Math.Min(pointA.Y, pointB.Y) 33 , Math.Abs(pointA.X - pointB.X) 34 , Math.Abs(pointA.Y - pointB.Y)); 35 } 36 37 private Color GetRandomColor() 38 { 39 return Color.FromArgb(random.Next(0, 256), random.Next(0, 256), random.Next(0, 256)); 40 } 41 42 private void ShowInformation(string message) 43 { 44 lblInformation.Text = message; 45 } 46 47 private void btnChangePenColor_Click(object sender, EventArgs e) 48 { 49 if (colors.ShowDialog(this) == DialogResult.OK) 50 { 51 penColor = colors.Color; 52 } 53 } 54 55 private void btnSwitchDoubleBuffered_Click(object sender, EventArgs e) 56 { 57 DoubleBuffered = !DoubleBuffered; 58 59 ShowInformation($"二级缓冲:{DoubleBuffered}。"); 60 } 61 62 private void btnDrawRandomEllipse_Click(object sender, EventArgs e) 63 { 64 var rectangle = GetRandomRectangle(); 65 66 var style = (DashStyle)(random.Next(0, 6)); 67 var dashCaps = new List<int> { 0, 2, 3 }; 68 var dashCap = (DashCap)dashCaps[random.Next(0, 3)]; 69 70 using (var g = CreateGraphics()) 71 using (var pen = new Pen(penColor, 4f)) 72 using (var brush = new LinearGradientBrush(rectangle, Color.Red, Color.Blue, LinearGradientMode.ForwardDiagonal)) 73 { 74 g.Clear(SystemColors.AppWorkspace); 75 g.SmoothingMode = SmoothingMode.HighQuality; 76 pen.DashStyle = style; 77 pen.DashCap = dashCap; 78 g.DrawEllipse(pen, rectangle); 79 } 80 81 ShowInformation($"随机椭圆,{rectangle},虚线冒:{dashCap.ToString()},线条样式:{style.ToString()}。"); 82 } 83 84 private void btnDrawRandomCircle_Click(object sender, EventArgs e) 85 { 86 var rectangle = GetRandomRectangle(); 87 var diameter = Math.Min(rectangle.Size.Width, rectangle.Size.Height); 88 rectangle = new Rectangle(rectangle.Location, new Size(diameter, diameter)); 89 90 var style = (DashStyle)(random.Next(0, 6)); 91 var dashCaps = new List<int> { 0, 2, 3 }; 92 var dashCap = (DashCap)dashCaps[random.Next(0, 3)]; 93 94 using (var g = CreateGraphics()) 95 using (var pen = new Pen(penColor, 4f)) 96 using (var brush = new LinearGradientBrush(rectangle, Color.Red, Color.Blue, LinearGradientMode.ForwardDiagonal)) 97 { 98 g.Clear(SystemColors.AppWorkspace); 99 g.SmoothingMode = SmoothingMode.HighQuality; 100 pen.DashStyle = style; 101 pen.DashCap = dashCap; 102 g.DrawEllipse(pen, rectangle); 103 } 104 105 ShowInformation($"正圆,{rectangle},虚线冒:{dashCap.ToString()},线条样式:{style.ToString()}。"); 106 } 107 108 private void btnFillWithPathGradientBrush_Click(object sender, EventArgs e) 109 { 110 var rectangle = GetRandomRectangle(); 111 112 var wrapMode = (WrapMode)(random.Next(0, 5)); 113 114 //var points = new Point[] { GetRandomPoint(), GetRandomPoint(), GetRandomPoint(), GetRandomPoint(), GetRandomPoint() }; 115 var points = new Point[] { GetRandomPoint(), GetRandomPoint(), GetRandomPoint() }; 116 117 using (var g = CreateGraphics()) 118 using (var brush = new PathGradientBrush(points, wrapMode)) 119 { 120 g.Clear(SystemColors.AppWorkspace); 121 g.SmoothingMode = SmoothingMode.HighQuality; 122 g.DrawEllipse(Pens.LightGray, rectangle); 123 g.FillEllipse(brush, rectangle); 124 } 125 126 ShowInformation($"路径填充,{rectangle},WrapMode:{wrapMode}。"); 127 } 128 } 129 }
同样,一个窗体项目,窗体的布局风格与上一节的保持一致:
几个方法不细说,直接上几个效果图草草了事……
正圆就是一种特殊的椭圆,它的rectangle的宽与高相等;
随机路径填充也蛮有意思的,有的图画,我都想直接拿来做logo了:)
好了,画圆的部分,内容就这么多,按照惯例,那么我们就来利用现有的知识,再耍个花活,画个贼溜溜的眼珠:)
private int maxDistance = 0; // 最大移动距离,用来衡量鼠标距离眼球中心位置的极限;以这个距离极限,来等比缩放眼球移动的距离; private Rectangle leftEyeBorderRect = Rectangle.Empty // 左眼眼眶 , rightEyeBorderRect = Rectangle.Empty // 右眼眼眶 , leftEyeBackRect = Rectangle.Empty // 左眼兰眼球 , rightEyeBackRect = Rectangle.Empty // 右眼兰眼球 , leftEyeFrontRect = Rectangle.Empty // 左眼黑眼珠 , rightEyeFrontRect = Rectangle.Empty; // 右眼黑眼珠
注释里说明了这几个变量的意义;
1 private void btnDrawFollowMouseEyes_Click(object sender, EventArgs e) 2 { 3 var center = new Point(ClientRectangle.Width / 2, (ClientRectangle.Height - pnlToolbox.Height) / 2); 4 leftEyeBorderRect = new Rectangle(center.X - 250, center.Y - 40, 200, 80); 5 rightEyeBorderRect = new Rectangle(center.X + 50, center.Y - 40, 200, 80); 6 maxDistance = ClientRectangle.Width < (ClientRectangle.Height - pnlToolbox.Height) 7 ? center.X - 150 - ClientRectangle.X 8 : center.Y - ClientRectangle.Y; 9 10 var style = (DashStyle)(random.Next(0, 6)); 11 var dashCaps = new List<int> { 0, 2, 3 }; 12 var dashCap = (DashCap)dashCaps[random.Next(0, 3)]; 13 14 using (var g = CreateGraphics()) 15 using (var pen = new Pen(penColor, 2f)) 16 using (var brush = new SolidBrush(penColor)) 17 { 18 g.Clear(SystemColors.AppWorkspace); 19 g.SmoothingMode = SmoothingMode.HighQuality; 20 pen.DashStyle = style; 21 pen.DashCap = dashCap; 22 g.FillEllipse(SystemBrushes.ControlLight, leftEyeBorderRect); 23 g.DrawEllipse(pen, leftEyeBorderRect); 24 g.FillEllipse(SystemBrushes.ControlLight, rightEyeBorderRect); 25 g.DrawEllipse(pen, rightEyeBorderRect); 26 } 27 28 FormDrawEllipses_MouseMove(null, new MouseEventArgs(MouseButtons.Left, 0, 0, 0, 0)); 29 30 ShowInformation($"贼溜溜的眼睛,跟随鼠标移动的眼睛示例。"); 31 }
这个事件中,主要就是画出眼眶和填充眼白;
1 private void FormDrawEllipses_MouseMove(object sender, MouseEventArgs e) 2 { 3 if (Rectangle.Empty.Equals(leftEyeBorderRect) || Rectangle.Empty.Equals(rightEyeBorderRect)) 4 return; 5 6 using (var g = CreateGraphics()) 7 using (var pen = new Pen(penColor, 2f)) 8 using (var brush = new SolidBrush(penColor)) 9 { 10 if (Rectangle.Empty != leftEyeBackRect) 11 g.FillEllipse(SystemBrushes.ControlLight, leftEyeBackRect); 12 if (Rectangle.Empty != rightEyeBackRect) 13 g.FillEllipse(SystemBrushes.ControlLight, rightEyeBackRect); 14 leftEyeBackRect = CalcRect(leftEyeBorderRect, e.Location, maxDistance, 15, 25); 15 rightEyeBackRect = CalcRect(rightEyeBorderRect, e.Location, maxDistance, 15, 25); 16 leftEyeFrontRect = CalcRect(leftEyeBackRect, e.Location, maxDistance, 15, 5); 17 rightEyeFrontRect = CalcRect(rightEyeBackRect, e.Location, maxDistance, 15, 5); 18 19 brush.Color = Color.Blue; 20 g.FillEllipse(brush, leftEyeBackRect); 21 g.FillEllipse(brush, rightEyeBackRect); 22 23 brush.Color = Color.Black; 24 g.FillEllipse(brush, leftEyeFrontRect); 25 g.FillEllipse(brush, rightEyeFrontRect); 26 27 g.Flush(); 28 } 29 }
这个事件里,首先就是将上一次兰眼球的范围填充成眼白的颜色,然后就是根据当前鼠标位置,重新计算兰眼球和黑眼珠的位置,然后进行填充。
这样就形成了一个动态效果了。
1 private Rectangle CalcRect(Rectangle baseRect, Point mouseLocation, int maxDistance, int maxMoveDistance, int radius) 2 { 3 var baseCenter = Point.Add(baseRect.Location, new Size(baseRect.Width / 2, baseRect.Height / 2)); 4 5 var radian = Math.Atan2((mouseLocation.Y - baseCenter.Y), (mouseLocation.X - baseCenter.X)); 6 7 var mouseDistance = Math.Min(maxDistance, Math.Sqrt(Math.Pow(mouseLocation.X - baseCenter.X, 2) + Math.Pow(mouseLocation.Y - baseCenter.Y, 2))); 8 9 var moveDistance = maxMoveDistance * (mouseDistance / maxDistance); 10 11 var targetCenter = new Point((int)Math.Ceiling(moveDistance * Math.Cos(radian) + baseCenter.X), (int)Math.Ceiling(moveDistance * Math.Sin(radian)) + baseCenter.Y); 12 13 return new Rectangle(targetCenter.X - radius, targetCenter.Y - radius, radius * 2, radius * 2); 14 }
OK,完活,收工。
喜欢本系列丛书的朋友,可以点击链接加入QQ交流群(994761602)【C# 破境之道】
方便各位在有疑问的时候可以及时给我个反馈。同时,也算是给各位志同道合的朋友提供一个交流的平台。
需要源码的童鞋,也可以在群文件中获取最新源代码。
感谢您的阅读。
《ASP.NET MVC 5 破境之道》
《C# 爬虫 破境之道》
《C# GDI+ 破境之道》
持续添加中……
喜欢本系列丛书的朋友,可以点击链接加入QQ交流群(994761602)【C# 破境之道】