Files
1258/JinYuan.ControlLib/UCRollText.cs
T

321 lines
9.3 KiB
C#
Raw Normal View History

2026-08-04 18:36:40 +08:00
using AntdUI;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Text;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
namespace JinYuan.ControlLib
{
public partial class UCRollText : UserControl
{
private Rectangle _textRect; // 文字绘制矩形
private int _moveDirection = 1; // 移动方向
private const int MIN_MOVE_PIXELS = 1;
private const int DEFAULT_MOVE_STEP = 2; // 减小步长,使滚动更平滑
private const int MIN_INTERVAL = 80; // 最小间隔 16ms ≈ 60fps
private Timer _timer;
private long _lastTickTime;
private bool _isAnimating = false; // 用于标记是否有异步操作正在执行,避免并发问题
private string _lastText = ""; // 缓存上次文本,避免重复测量
private Size _textSize; // 缓存文本尺寸
#region Properties
public override Font Font
{
get => base.Font;
set
{
base.Font = value;
_textSize = Size.Empty; // 字体改变,需要重新测量
UpdateTextRect();
}
}
public override string Text
{
get => base.Text;
set
{
if (base.Text != value)
{
base.Text = value;
_lastText = value;
_textSize = Size.Empty; // 文本改变,需要重新测量
UpdateTextRect();
if (_isChangeReset) ResetTextPosition();
}
}
}
private RollStyle _rollStyle = RollStyle.LeftToRight;
public RollStyle RollStyle
{
get => _rollStyle;
set
{
if (_rollStyle != value)
{
_rollStyle = value;
_moveDirection = value == RollStyle.RightToLeft ? -1 : 1;
ResetTextPosition();
}
}
}
private bool _isChangeReset = true;
[Description("文本改变是否重新从边缘运动"), Category("自定义")]
public bool IsChangeReset
{
get => _isChangeReset;
set => _isChangeReset = value;
}
private bool _isExit;
[Description("是否退出"), Category("自定义")]
public bool IsExit
{
get => _isExit;
set
{
_isExit = value;
if (value) StopAnimation();
}
}
private int _moveInterval = MIN_INTERVAL;
[Description("每次滚动间隔时间,越小速度越快"), Category("自定义")]
public int MoveInterval
{
get => _moveInterval;
set
{
_moveInterval = Math.Max(value, MIN_INTERVAL);
if (_timer != null)
{
_timer.Interval = _moveInterval;
}
}
}
#endregion
public UCRollText()
{
InitializeControl();
StartAnimation();
}
private void InitializeControl()
{
// 高性能绘制样式
SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint |
ControlStyles.SupportsTransparentBackColor, true);
DoubleBuffered = true;
Size = new Size(450, 30);
Text = "滚动文字";
ForeColor = Color.FromArgb(255, 77, 59);
}
private void StartAnimation()
{
if (_isAnimating || _timer != null) return;
_isAnimating = true;
_lastTickTime = Stopwatch.GetTimestamp();
_timer = new Timer();
_timer.Interval = _moveInterval;
_timer.Tick += Timer_Tick;
_timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
if (!_isAnimating || !Visible || this.IsDisposed) return;
try
{
long now = Stopwatch.GetTimestamp();
long elapsedMs = (now - _lastTickTime) * 1000 / Stopwatch.Frequency;
_lastTickTime = now;
// 根据经过的时间计算平滑步长
int step = (int)(DEFAULT_MOVE_STEP * (elapsedMs / (float)_moveInterval));
if (step < 1) step = 1;
UpdateTextPosition(step);
//// 调试输出(发布时可注释)
//Console.WriteLine($"UCRollText Tick elapsed={elapsedMs}ms");
}
catch { }
}
private void StopAnimation()
{
_isAnimating = false;
if (_timer != null)
{
_timer.Stop();
_timer.Dispose();
_timer = null;
}
}
private void UpdateTextPosition(int step)
{
if (_textRect.IsEmpty) return;
// 只有在BackAndForth模式下才检查文字宽度
if (_rollStyle == RollStyle.BackAndForth && _textRect.Width >= Width) return;
int oldX = _textRect.X;
// 更新位置
_textRect.X += step * _moveDirection;
// 边界处理
switch (_rollStyle)
{
case RollStyle.BackAndForth:
if (_textRect.X <= 0)
_moveDirection = 1;
else if (_textRect.Right >= Width)
_moveDirection = -1;
break;
case RollStyle.LeftToRight:
if (_textRect.X > Width)
_textRect.X = -_textRect.Width - 1;
break;
case RollStyle.RightToLeft:
if (_textRect.Right < 0)
_textRect.X = Width;
break;
}
// 只有位置变化才重绘
if (Math.Abs(_textRect.X - oldX) >= MIN_MOVE_PIXELS)
{
if (IsHandleCreated && !IsDisposed && Visible)
{
Invalidate();
}
}
}
private void UpdateTextRect()
{
if (string.IsNullOrEmpty(Text))
{
_textRect = Rectangle.Empty;
return;
}
// 只有在文本或字体改变时才重新测量
if (_textSize.IsEmpty || Text != _lastText)
{
_textSize = TextRenderer.MeasureText(Text, Font);
_lastText = Text;
}
_textRect = new Rectangle(
_isChangeReset ? 0 : _textRect.X,
(Height - _textSize.Height) / 2,
_textSize.Width,
_textSize.Height
);
if (IsHandleCreated && Visible)
Invalidate();
}
private void ResetTextPosition()
{
if (_textRect.IsEmpty) return;
switch (_rollStyle)
{
case RollStyle.LeftToRight:
_textRect.X = -_textRect.Width - 1;
break;
case RollStyle.RightToLeft:
_textRect.X = Width;
break;
default:
_textRect.X = 0;
break;
}
if (IsHandleCreated && Visible)
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
// 禁用基类绘制,减少绘制层次
// base.OnPaint(e);
if (!_textRect.IsEmpty && !string.IsNullOrEmpty(Text) && Visible)
{
// 使用TextRenderer的优化方法,指定文本格式
TextRenderer.DrawText(e.Graphics, Text, Font, _textRect, ForeColor, TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.NoClipping);
}
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
if (_textRect != Rectangle.Empty)
{
_textRect.Y = (Height - _textRect.Height) / 2;
Invalidate();
}
}
protected override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);
// 控件不可见时停止动画,节省资源
if (Visible)
{
StartAnimation();
}
else
{
StopAnimation();
}
}
protected override void OnHandleDestroyed(EventArgs e)
{
StopAnimation();
base.OnHandleDestroyed(e);
}
private void InitializeComponent()
{
this.SuspendLayout();
this.Name = "UCRollText";
this.Size = new Size(450, 30);
this.ResumeLayout(false);
}
}
public enum RollStyle
{
LeftToRight,
RightToLeft,
BackAndForth
}
}