C# Windows Forms中实现风羽的技术方案咨询
解决C# Windows Forms中风羽绘制的定位与旋转问题
问题描述
在C# Windows Forms项目中需要绘制/展示风羽,尝试使用ESRI Weather字体时遇到定位不准确和旋转混乱的问题,期望实现风羽末端固定在交叉点且不受角度调整影响。同时考虑过SVG图片方案,但未找到合适的免费资源,附上相关代码寻求解决思路。
用户提供的测试代码:
int _rotcount = 0; int _rotlog = 80; private void button2_Click(object sender, EventArgs e) { Font fn = new Font("ESRI Weather", 40,FontStyle.Bold); string text = ((char)0227).ToString(); Graphics gr = PnlBase.CreateGraphics(); GraphicsState st = gr.Save(); gr.ResetTransform(); if (_rotcount > 0) { _rotlog += 10; gr.RotateTransform(270); _rotcount++; } else { _rotlog += 10; gr.RotateTransform(180); _rotcount++; } gr.TranslateTransform((float)55, (float)50,MatrixOrder.Append); Pen blackPen = new Pen(Color.Black, 3); using (StringFormat sf = new StringFormat()) { sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Center; gr.DrawString(text, fn, new SolidBrush(Color.Black), 0, 0, sf); } gr.Restore(st); }
解决方案
一、修复ESRI Weather字体的旋转与定位问题
原代码核心问题是变换顺序错误:先旋转再平移会导致锚点偏移。正确逻辑是先平移到目标交叉点,再围绕风羽末端旋转,最后调整文本位置确保末端固定。
修改后的代码:
int _rotcount = 0; int currentAngle = 180; // 初始角度,需根据ESRI字体的风向定义调整 private void button2_Click(object sender, EventArgs e) { using (Font fn = new Font("ESRI Weather", 40, FontStyle.Bold)) using (Graphics gr = PnlBase.CreateGraphics()) using (SolidBrush brush = new SolidBrush(Color.Black)) { gr.Clear(PnlBase.BackColor); // 清除画布避免重叠 string text = ((char)0227).ToString(); // 计算文本尺寸,确定风羽末端(交叉点)的锚点位置 SizeF textSize = gr.MeasureString(text, fn); PointF anchor = new PointF(textSize.Width / 2, textSize.Height); // 锚点设为文本中心底部,可根据字体微调 GraphicsState st = gr.Save(); try { // 1. 平移到目标交叉点(55,50) gr.TranslateTransform(55, 50); // 2. 围绕锚点旋转角度 gr.RotateTransform(currentAngle, MatrixOrder.Append); // 3. 反向平移,让风羽末端刚好落在交叉点 gr.TranslateTransform(-anchor.X, -anchor.Y, MatrixOrder.Append); // 绘制风羽,用左上角对齐避免偏移 using (StringFormat sf = new StringFormat()) { sf.Alignment = StringAlignment.Near; sf.LineAlignment = StringAlignment.Near; gr.DrawString(text, fn, brush, 0, 0, sf); } // 绘制红色标记验证交叉点位置 using (Pen pen = new Pen(Color.Red, 3)) { gr.ResetTransform(); gr.DrawEllipse(pen, 55 - 3, 50 - 3, 6, 6); } } finally { gr.Restore(st); } // 更新角度,循环360度 currentAngle += 10; if (currentAngle >= 360) currentAngle = 0; _rotcount++; } }
二、自定义绘制风羽(最可控方案)
如果字体依赖不稳定,直接用GDI+绘制风羽是最优解,完全自定义形状、锚点和旋转逻辑,无需外部资源:
private void DrawWindBarb(Graphics gr, PointF fixedAnchor, float windAngle, float windSpeed) { gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; using (Pen pen = new Pen(Color.Black, 2)) { GraphicsState st = gr.Save(); try { // 平移到固定交叉点,再旋转到风向角度 gr.TranslateTransform(fixedAnchor.X, fixedAnchor.Y); gr.RotateTransform(windAngle); // 绘制风杆(从交叉点向外延伸) const float shaftLength = 50; gr.DrawLine(pen, 0, 0, shaftLength, 0); // 根据风速绘制风羽:长羽=10节,短羽=5节 const float barbLength = 15; const float barbSpacing = 5; int fullBarbs = (int)(windSpeed / 10); int halfBarbs = (int)((windSpeed % 10) / 5); // 绘制长羽 for (int i = 0; i < fullBarbs; i++) { float yPos = -barbSpacing * (i + 1); gr.DrawLine(pen, shaftLength - barbLength, yPos, shaftLength, 0); } // 绘制短羽 for (int i = 0; i < halfBarbs; i++) { float yPos = -barbSpacing * (fullBarbs + i + 1); gr.DrawLine(pen, shaftLength - barbLength / 2, yPos, shaftLength, 0); } } finally { gr.Restore(st); } } } // 使用示例 private void button2_Click(object sender, EventArgs e) { static int currentAngle = 180; using (Graphics gr = PnlBase.CreateGraphics()) { gr.Clear(PnlBase.BackColor); // 固定交叉点在(55,50),风速25节,角度每次加10度 DrawWindBarb(gr, new PointF(55, 50), currentAngle, 25); currentAngle += 10; if (currentAngle >= 360) currentAngle = 0; } }
该方案优势:
- 交叉点100%固定,旋转无偏移
- 可直接根据风速动态生成对应风羽
- 无需依赖任何外部资源,兼容性强
三、免费SVG风羽资源方案
若坚持使用SVG:
- 用免费矢量工具(如Inkscape)自行绘制:将交叉点设为SVG原点,导出后用
SvgImageBox等控件加载,旋转时围绕原点即可固定位置 - 搜索开源气象项目:部分GitHub气象可视化仓库会提供免费风羽SVG资源
- 免费矢量图库:筛选"wind barb"关键词,选择可商用的SVG文件
内容的提问来源于stack exchange,提问作者saeed gholampoor
相关产品推荐
相关产品推荐

