关于平滑曲线,相信很多人在网上提过这个问题,如何画出平滑曲线?我也很想知道答案,究竟如何才能在MouseMove事件里画出真正平滑又柔和的曲线?注意本文讨论的重点是在MouseMove事件里,并非相对固定的几个点
针对这个问题,网上给出了很多种方案。看到最多的是用类似gdi的moveto,lineto实现,gdi+则是drawline,但这个方案有一个很明显的漏洞:在MouseMove事件里,当用户快速移动鼠标时,我们可以很明显的看到画出的曲线是由直线段拼接而成,一点都不圆滑,这和我们的主题相差太远,所以否决这种方案。
当然网上也给出了另一种方案:DrawCurve
第一次看到这个方法,我就像发现新大陆一样,因为网上给出的效果图确实非常圆滑。
但很可惜,实践证明,用DrawCurve方法在相对固定的几个点下画曲线确实平滑,但一旦移到MouseMove事件里却出现了各种各样的问题,比如锯齿,“毛刺”等,下图就非常直观的呈现出了这些特征
有问题理应要有相应的解决方案,很荣幸,我们找到了Pen的线帽(StartCap/EndCap)和联接样式(LineJoin)两个属性,这两个恰恰是刚刚提到那两个问题的“克星”,线帽用来对付锯齿,而联接样式则是用来对付“毛刺”,只要分别设置这三个属性为Round即可,缺一不可哈:)
未设置线帽的效果
未设置联接样式的效果
只有三个属性设全了,才能画出“真正”完美的平滑曲线
附上测试代码,有兴趣的读者不妨一试
- public partial class Form1 : Form
- {
- private bool initial = true, startDraw;
- List<Point> pList = new List<Point>();
-
- public Form1()
- {
- InitializeComponent();
- }
-
- protected override void OnPaint(PaintEventArgs e)
- {
- base.OnPaint(e);
- if (initial)
- {
- return;
- }
- Graphics g = e.Graphics;
- g.SmoothingMode = SmoothingMode.AntiAlias;
- using (Pen p = new Pen(Color.Black, 15))
- {
-
- p.StartCap = LineCap.Round;
- p.EndCap = LineCap.Round;
-
-
- p.LineJoin = LineJoin.Round;
-
- g.DrawCurve(p, pList.ToArray());
- }
- }
-
- private void Form1_MouseDown(object sender, MouseEventArgs e)
- {
- if (e.Button == MouseButtons.Left)
- {
- initial = false;
- startDraw = true;
- pList.Add(e.Location);
- }
- }
-
- private void Form1_MouseMove(object sender, MouseEventArgs e)
- {
- if (e.Button == MouseButtons.Left && startDraw)
- {
- pList.Add(e.Location);
- this.Refresh();
- }
- }
-
- private void Form1_MouseUp(object sender, MouseEventArgs e)
- {
- if (e.Button == MouseButtons.Left)
- {
- startDraw = false;
- }
- }
- }