《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);
看到了吧,就换了个方法名,参数连名都没换:)
所以,了解了如何画矩形,画圆形就是自然掌握的了。
本节就不多费口舌重复了:
namespace MikeWare.GdiPlus.Ellipses
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms; public partial class FormDrawEllipses : Form
{
private Random random = null;
private Color penColor = Color.Transparent; public FormDrawEllipses()
{
InitializeComponent();
random = new Random(DateTime.Now.Millisecond);
penColor = GetRandomColor();
} private Point GetRandomPoint()
{
return new Point(random.Next(, ClientRectangle.Width), random.Next(, ClientRectangle.Height - pnlToolbox.Height));
} private Rectangle GetRandomRectangle()
{
var pointA = GetRandomPoint();
var pointB = GetRandomPoint(); return new Rectangle(Math.Min(pointA.X, pointB.X)
, Math.Min(pointA.Y, pointB.Y)
, Math.Abs(pointA.X - pointB.X)
, Math.Abs(pointA.Y - pointB.Y));
} private Color GetRandomColor()
{
return Color.FromArgb(random.Next(, ), random.Next(, ), random.Next(, ));
} private void ShowInformation(string message)
{
lblInformation.Text = message;
} private void btnChangePenColor_Click(object sender, EventArgs e)
{
if (colors.ShowDialog(this) == DialogResult.OK)
{
penColor = colors.Color;
}
} private void btnSwitchDoubleBuffered_Click(object sender, EventArgs e)
{
DoubleBuffered = !DoubleBuffered; ShowInformation($"二级缓冲:{DoubleBuffered}。");
} private void btnDrawRandomEllipse_Click(object sender, EventArgs e)
{
var rectangle = GetRandomRectangle(); var style = (DashStyle)(random.Next(, ));
var dashCaps = new List<int> { , , };
var dashCap = (DashCap)dashCaps[random.Next(, )]; using (var g = CreateGraphics())
using (var pen = new Pen(penColor, 4f))
using (var brush = new LinearGradientBrush(rectangle, Color.Red, Color.Blue, LinearGradientMode.ForwardDiagonal))
{
g.Clear(SystemColors.AppWorkspace);
g.SmoothingMode = SmoothingMode.HighQuality;
pen.DashStyle = style;
pen.DashCap = dashCap;
g.DrawEllipse(pen, rectangle);
} ShowInformation($"随机椭圆,{rectangle},虚线冒:{dashCap.ToString()},线条样式:{style.ToString()}。");
} private void btnDrawRandomCircle_Click(object sender, EventArgs e)
{
var rectangle = GetRandomRectangle();
var diameter = Math.Min(rectangle.Size.Width, rectangle.Size.Height);
rectangle = new Rectangle(rectangle.Location, new Size(diameter, diameter)); var style = (DashStyle)(random.Next(, ));
var dashCaps = new List<int> { , , };
var dashCap = (DashCap)dashCaps[random.Next(, )]; using (var g = CreateGraphics())
using (var pen = new Pen(penColor, 4f))
using (var brush = new LinearGradientBrush(rectangle, Color.Red, Color.Blue, LinearGradientMode.ForwardDiagonal))
{
g.Clear(SystemColors.AppWorkspace);
g.SmoothingMode = SmoothingMode.HighQuality;
pen.DashStyle = style;
pen.DashCap = dashCap;
g.DrawEllipse(pen, rectangle);
} ShowInformation($"正圆,{rectangle},虚线冒:{dashCap.ToString()},线条样式:{style.ToString()}。");
} private void btnFillWithPathGradientBrush_Click(object sender, EventArgs e)
{
var rectangle = GetRandomRectangle(); var wrapMode = (WrapMode)(random.Next(, )); //var points = new Point[] { GetRandomPoint(), GetRandomPoint(), GetRandomPoint(), GetRandomPoint(), GetRandomPoint() };
var points = new Point[] { GetRandomPoint(), GetRandomPoint(), GetRandomPoint() }; using (var g = CreateGraphics())
using (var brush = new PathGradientBrush(points, wrapMode))
{
g.Clear(SystemColors.AppWorkspace);
g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawEllipse(Pens.LightGray, rectangle);
g.FillEllipse(brush, rectangle);
} ShowInformation($"路径填充,{rectangle},WrapMode:{wrapMode}。");
}
}
}
画圆 —— FormDrawEllipses
同样,一个窗体项目,窗体的布局风格与上一节的保持一致:
几个方法不细说,直接上几个效果图草草了事……
正圆就是一种特殊的椭圆,它的rectangle的宽与高相等;
随机路径填充也蛮有意思的,有的图画,我都想直接那类做logo了:)
好了,画圆的部分,内容就这么多,按照惯例,那么我们就来利用现有的知识,再耍个花活,画个贼溜溜的眼珠:)
private int maxDistance = ; // 最大移动距离,用来衡量鼠标距离眼球中心位置的极限;以这个距离极限,来等比缩放眼球移动的距离;
private Rectangle leftEyeBorderRect = Rectangle.Empty // 左眼眼眶
, rightEyeBorderRect = Rectangle.Empty // 右眼眼眶
, leftEyeBackRect = Rectangle.Empty // 左眼兰眼球
, rightEyeBackRect = Rectangle.Empty // 右眼兰眼球
, leftEyeFrontRect = Rectangle.Empty // 左眼黑眼珠
, rightEyeFrontRect = Rectangle.Empty; // 右眼黑眼珠
定义几个私有变量
注释里说明了这几个变量的意义;
private void btnDrawFollowMouseEyes_Click(object sender, EventArgs e)
{
var center = new Point(ClientRectangle.Width / , (ClientRectangle.Height - pnlToolbox.Height) / );
leftEyeBorderRect = new Rectangle(center.X - , center.Y - , , );
rightEyeBorderRect = new Rectangle(center.X + , center.Y - , , );
maxDistance = ClientRectangle.Width < (ClientRectangle.Height - pnlToolbox.Height)
? center.X - - ClientRectangle.X
: center.Y - ClientRectangle.Y; var style = (DashStyle)(random.Next(, ));
var dashCaps = new List<int> { , , };
var dashCap = (DashCap)dashCaps[random.Next(, )]; using (var g = CreateGraphics())
using (var pen = new Pen(penColor, 2f))
using (var brush = new SolidBrush(penColor))
{
g.Clear(SystemColors.AppWorkspace);
g.SmoothingMode = SmoothingMode.HighQuality;
pen.DashStyle = style;
pen.DashCap = dashCap;
g.FillEllipse(SystemBrushes.ControlLight, leftEyeBorderRect);
g.DrawEllipse(pen, leftEyeBorderRect);
g.FillEllipse(SystemBrushes.ControlLight, rightEyeBorderRect);
g.DrawEllipse(pen, rightEyeBorderRect);
} FormDrawEllipses_MouseMove(null, new MouseEventArgs(MouseButtons.Left, , , , )); ShowInformation($"贼溜溜的眼睛,跟随鼠标移动的眼睛示例。");
}
贼眼珠按钮事件 —— btnDrawFollowMouseEyes_Click
这个事件中,主要就是画出眼眶和填充眼白;
private void FormDrawEllipses_MouseMove(object sender, MouseEventArgs e)
{
if (Rectangle.Empty.Equals(leftEyeBorderRect) || Rectangle.Empty.Equals(rightEyeBorderRect))
return; using (var g = CreateGraphics())
using (var pen = new Pen(penColor, 2f))
using (var brush = new SolidBrush(penColor))
{
if (Rectangle.Empty != leftEyeBackRect)
g.FillEllipse(SystemBrushes.ControlLight, leftEyeBackRect);
if (Rectangle.Empty != rightEyeBackRect)
g.FillEllipse(SystemBrushes.ControlLight, rightEyeBackRect);
leftEyeBackRect = CalcRect(leftEyeBorderRect, e.Location, maxDistance, , );
rightEyeBackRect = CalcRect(rightEyeBorderRect, e.Location, maxDistance, , );
leftEyeFrontRect = CalcRect(leftEyeBackRect, e.Location, maxDistance, , );
rightEyeFrontRect = CalcRect(rightEyeBackRect, e.Location, maxDistance, , ); brush.Color = Color.Blue;
g.FillEllipse(brush, leftEyeBackRect);
g.FillEllipse(brush, rightEyeBackRect); brush.Color = Color.Black;
g.FillEllipse(brush, leftEyeFrontRect);
g.FillEllipse(brush, rightEyeFrontRect); g.Flush();
}
}
窗体的鼠标移动事件 —— FormDrawEllipses_MouseMove
这个事件里,首先就是将上一次兰眼球的范围填充成眼白的颜色,然后就是根据当前鼠标位置,重新计算兰眼球和黑眼珠的位置,然后进行填充。
这样就形成了一个动态效果了。
private Rectangle CalcRect(Rectangle baseRect, Point mouseLocation, int maxDistance, int maxMoveDistance, int radius)
{
var baseCenter = Point.Add(baseRect.Location, new Size(baseRect.Width / , baseRect.Height / )); var radian = Math.Atan2((mouseLocation.Y - baseCenter.Y), (mouseLocation.X - baseCenter.X)); var mouseDistance = Math.Min(maxDistance, Math.Sqrt(Math.Pow(mouseLocation.X - baseCenter.X, ) + Math.Pow(mouseLocation.Y - baseCenter.Y, ))); var moveDistance = maxMoveDistance * (mouseDistance / maxDistance); var targetCenter = new Point((int)Math.Ceiling(moveDistance * Math.Cos(radian) + baseCenter.X), (int)Math.Ceiling(moveDistance * Math.Sin(radian)) + baseCenter.Y); return new Rectangle(targetCenter.X - radius, targetCenter.Y - radius, radius * , radius * );
}
辅助方法 —— 计算眼球和眼珠的矩形位置
OK,完活,收工。
喜欢本系列丛书的朋友,可以点击链接加入QQ交流群(994761602)【C# 破境之道】
方便各位在有疑问的时候可以及时给我个反馈。同时,也算是给各位志同道合的朋友提供一个交流的平台。
需要源码的童鞋,也可以在群文件中获取最新源代码。
《C# GDI+ 破境之道》:第一境 GDI+基础 —— 第三节:画圆形的更多相关文章
- 《C# GDI+ 破境之道》:第一境 GDI+基础 —— 第一节:画直线
今天正式开一本新书,<C# GDI+ 破镜之道>,同样是破镜之道系列丛书的一分子. 关于GDI+呢,官方的解释是这样的: GDI+ 是 Microsoft Windows 操作系统的窗体子 ...
- 《C# GDI+ 破境之道》:第一境 GDI+基础 —— 第二节:画矩形
有了上一节画线的基础,画矩形的各种边线就特别好理解了,所以,本节在矩形边线上,就不做过多的讲解了,关注一下画“随机矩形”的具体实现就好.与画线相比较,画矩形稍微复杂的一点就是在于它多了很多填充的样式. ...
- 《ASP.NET MVC 5 破境之道》:第一境 ASP.Net MVC5项目初探 — 第三节:View层简单改造
第一境 ASP.Net MVC5项目初探 — 第三节:View层简单改造 MVC默认模板的视觉设计从MVC1到MVC3都没有改变,比较陈旧了:在MVC4中做了升级,好看些,在不同的分辨率下,也能工作得 ...
- 《C# 爬虫 破境之道》:第二境 爬虫应用 — 第一节:HTTP协议数据采集
首先欢迎您来到本书的第二境,本境,我们将全力打造一个实际生产环境可用的爬虫应用了.虽然只是刚开始,虽然路漫漫其修远,不过还是有点小鸡冻:P 本境打算针对几大派生类做进一步深耕,包括与应用的结合.对比它 ...
- 《C# 爬虫 破境之道》:第一境 爬虫原理 — 第六节:第一境尾声
在第一境中,我们主要了解了爬虫的一些基本原理,说原理也行,说基础知识也罢,结果就是已经知道一个小爬虫是如何诞生的了~那么现在,请默默回想一下,在第一境中,您都掌握了哪些内容?哪些还比较模糊?如果还有什 ...
- 《C# 爬虫 破境之道》:第一境 爬虫原理 — 第五节:数据流处理的那些事儿
为什么说到数据流了呢,因为上一节中介绍了一下异步发送请求.同样,在数据流的处理上,C#也为我们提供几个有用的异步处理方法.而且,爬虫这生物,处理数据流是基础本能,比较重要.本着这个原则,就聊一聊吧. ...
- 《C# 爬虫 破境之道》:第一境 爬虫原理 — 第二节:WebRequest
本节主要来介绍一下,在C#中制造爬虫,最为常见.常用.实用的基础类 ------ WebRequest.WebResponse. 先来看一个示例 [1.2.1]: using System; usin ...
- 《C# 爬虫 破境之道》:第一境 爬虫原理 — 第一节:整体思路
在构建本章节内容的时候,笔者也在想一个问题,究竟什么样的采集器框架,才能算得上是一个“全能”的呢?就我自己以往项目经历而言,可以归纳以下几个大的分类: 根据通讯协议:HTTP的.HTTPS的.TCP的 ...
- 《C# 爬虫 破境之道》:第一境 爬虫原理 — 第四节:同步与异步请求方式
前两节,我们对WebRequest和WebResponse这两个类做了介绍,但两者还相对独立.本节,我们来说说如何将两者结合起来,方式有哪些,有什么不同. 1.4.1 说结合,无非就是我们如何发送一个 ...
随机推荐
- Spring Cloud(一):服务注册中心Eureka
Spring Cloud 基于 Netflix 的几个开源项目进行了封装,提供包括服务注册与发现(Eureka),智能路由(Zuul),熔断器(Hystrix),客户端负载均衡(Ribbon)等在内的 ...
- git版本管理工具(二)
1.查看历史版本 ·git log ·git reflog 2.版本回退 ·git reset --hard HEAD^(HEAD代表当前版本) ·HEAD^代表回退到上一个版本 以此类推 ·HEAD ...
- 使用C#交互快速生成代码!
#r "System.Reflection" #r "D:\xk.erp\OP.Model\bin\Debug\OP.Model.dll" using Syst ...
- Python开发 之 Websocket 的使用示例
1.唠唠叨叨 最近又回顾了下Websocket,发现已经忘的七七八八了.于是用js写了客户端,用python写了服务端,来复习一下这方面的知识. 2.先看一下效果吧 2.1.效果1(一个客户端连上服务 ...
- js最简单的编写地点
1. 在哪里? 在浏览器的控制台. 2. 有什么作用? 方便快捷的测试纯js代码语句. 3. 如何使用? Google浏览器为例: 按 F12键 打开 开发者工具 (或者 浏览器工具栏 => ...
- Java 1.7.0_06中String类内部实现的一些变化【转】
原文链接: java-performance 翻译: ImportNew.com- 夏千林译文链接: http://www.importnew.com/7656.html ChangeLog: 201 ...
- 【大道至简】NetCore3.1快速开发框架一:搭建框架
这一章,我们直接创建NetCore3.1的项目 主要分为1个Api项目,和几个类库 解释: 项目——FytSoa.Api:提供前端接口的Api项目 类库——FytSoa.Core:包含了数据库操作类和 ...
- Mysql的binlog日志与mysqlbinlog命令
binlog相关 MySQL 的二进制日志 binlog 可以说是 MySQL 最重要的日志,它记录了所有的 DDL 和 DML 语句(除了数据查询语句select.show等),以事件形式记录,还包 ...
- Heroku学习 - 利用Heroku app实现 OrgA 查询Org B 的数据数据
最近研究了一番如何通过Heroku应用对OrgA开放一个接口,参数传递的是一个SQL,APP的负责将SQL通过callout的形式调用目标OrgB Rest API来获取数据并返回给OrgA.我是用的 ...
- Qt Installer Framework翻译(3-5)
指定设置选项 设置页面使用户可以指定代理设置或安装附加组件. 用户在简介页面上点击"设置"来指定设置选项. 指定代理设置 默认情况下,安装程序使用系统代理设置.用户可以选择不使用或 ...