关于平滑曲线,相信很多人在网上提过这个问题,如何画出平滑曲线?我也很想知道答案,究竟如何才能在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; } } }