321 lines
9.3 KiB
C#
321 lines
9.3 KiB
C#
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
|
|
}
|
|
} |