Skip to content
This repository was archived by the owner on Jan 25, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions PCL.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,11 @@
<!-- 自定义代码生成目标 -->
<Target Name="GenerateTypes" BeforeTargets="CoreCompile">
<Message Text="Generating LifecycleServiceTypes class..." Importance="normal" />
<Exec Command="pwsh -ExecutionPolicy Bypass -File &quot;$(MSBuildProjectDirectory)\App\LifecycleServiceTypesGenerator.ps1&quot; &quot;$(MSBuildProjectDirectory)&quot;"
ContinueOnError="false" />
<Exec Command="pwsh -ExecutionPolicy Bypass -File &quot;$(MSBuildProjectDirectory)\App\LifecycleServiceTypesGenerator.ps1&quot; &quot;$(MSBuildProjectDirectory)&quot;" ContinueOnError="false" />
<Message Text="Generating setup accessors..." Importance="normal" />
<Exec Command="pwsh -ExecutionPolicy Bypass -File &quot;$(MSBuildProjectDirectory)\ProgramSetup\SetupAccessorGenerator.ps1&quot; &quot;$(MSBuildProjectDirectory)&quot;"
ContinueOnError="false" />
<Exec Command="pwsh -ExecutionPolicy Bypass -File &quot;$(MSBuildProjectDirectory)\ProgramSetup\SetupAccessorGenerator.ps1&quot; &quot;$(MSBuildProjectDirectory)&quot;" ContinueOnError="false" />
<Message Text="Generating environment secrets..." Importance="normal" />
<Exec Command="pwsh -ExecutionPolicy Bypass -File &quot;$(MSBuildProjectDirectory)\Utils\OS\EnvironmentInteropGenerator.ps1&quot; &quot;$(MSBuildProjectDirectory)&quot;"
ContinueOnError="false" />
<Exec Command="pwsh -ExecutionPolicy Bypass -File &quot;$(MSBuildProjectDirectory)\Utils\OS\EnvironmentInteropGenerator.ps1&quot; &quot;$(MSBuildProjectDirectory)&quot;" ContinueOnError="false" />
<ItemGroup>
<Compile Include="**\*.g.cs" Exclude="bin\**;obj\**;publish\**" />
</ItemGroup>
Expand Down
71 changes: 71 additions & 0 deletions UI/Animation/AniData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;

namespace PCL.Core.UI.Animation;

/// <summary>
/// 单个动画对象。
/// </summary>
/// <remarks></remarks>
public struct AniData
{

/// <summary>
/// 动画种类。
/// </summary>
/// <remarks></remarks>
public AniType TypeMain;
/// <summary>
/// 动画副种类。
/// </summary>
/// <remarks></remarks>
public AniTypeExt TypeSub;

/// <summary>
/// 动画总长度。
/// </summary>
/// <remarks></remarks>
public int TimeTotal;
/// <summary>
/// 已经执行的动画长度。如果为负数则为延迟。
/// </summary>
/// <remarks></remarks>
public int TimeFinished;
/// <summary>
/// 已经完成的百分比。
/// </summary>
/// <remarks></remarks>
public double TimePercent;

/// <summary>
/// 是否为“以后”。
/// </summary>
/// <remarks></remarks>
public bool IsAfter;

/// <summary>
/// 插值器类型。
/// </summary>
/// <remarks></remarks>
public AniEase Ease;
/// <summary>
/// 动画对象。
/// </summary>
/// <remarks></remarks>
public object Obj;
/// <summary>
/// 动画值。
/// </summary>
/// <remarks></remarks>
public object Value;
/// <summary>
/// 上次执行时的动画值。
/// </summary>
/// <remarks></remarks>
public object ValueLast;

public override readonly string ToString()

Check notice on line 66 in UI/Animation/AniData.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Adjust modifiers declaration order

Inconsistent modifiers declaration order
{
Enum enumData = (Enum)TypeMain;

Check warning on line 68 in UI/Animation/AniData.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant cast

Type cast is redundant

Check notice on line 68 in UI/Animation/AniData.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use preferred 'var' style (when type is simple)

Use 'var' (simple types)
return Enum.GetName(enumData.GetType(), enumData) + " | " + TimeFinished + "/" + TimeTotal + "(" + Math.Round(TimePercent * 100d) + "%)" + (Obj is null ? "" : " | " + Obj.ToString() + "(" + Obj.GetType().Name + ")");

Check warning on line 69 in UI/Animation/AniData.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant 'object.ToString()' call

Redundant 'Object.ToString()' call
}
}
195 changes: 195 additions & 0 deletions UI/Animation/AniEase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
using System;
using PCL.Core.Utils;

namespace PCL.Core.UI.Animation;

// 基类
public enum AniEasePower : int

Check warning on line 7 in UI/Animation/AniEase.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Underlying type of enum is 'int'

'int' is default enum governing type
{
Weak = 2,
Middle = 3,
Strong = 4,
ExtraStrong = 5
}

/// <summary>
/// 缓动函数基类。
/// </summary>
public abstract class AniEase
{
/// <summary>
/// 获取函数值。
/// </summary>
/// <param name="t">时间百分比。</param>
public abstract double GetValue(double t);
/// <summary>
/// 获取增量值。
/// </summary>
/// <param name="t1">较大的 X。</param>
/// <param name="t0">较小的 X。</param>
public virtual double GetDelta(double t1, double t0)
=> GetValue(t1) - GetValue(t0);
}

/// <summary>
/// 渐入渐出组合。
/// </summary>
public class AniEaseInout(AniEase easeIn, AniEase easeOut, double easeInPercent = 0.5d) : AniEase
{
private readonly AniEase _easeIn = easeIn;

Check notice on line 39 in UI/Animation/AniEase.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Replace with primary constructor parameter

Replace with primary constructor parameter
private readonly AniEase _easeOut = easeOut;

Check notice on line 40 in UI/Animation/AniEase.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Replace with primary constructor parameter

Replace with primary constructor parameter
private readonly double _easeInPercent = easeInPercent;

Check notice on line 41 in UI/Animation/AniEase.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Replace with primary constructor parameter

Replace with primary constructor parameter

public override double GetValue(double t)
{
if (t < _easeInPercent)
return _easeInPercent * _easeIn.GetValue(t / _easeInPercent);
return (1d - _easeInPercent) * _easeOut.GetValue((t - _easeInPercent) / (1d - _easeInPercent)) + _easeInPercent;
}
}

// Linear / 线性
/// <summary>
/// 线性,无缓动。
/// </summary>
public class AniEaseLinear : AniEase
{
public override double GetValue(double t)
=> MathUtils.Clamp(t, 0d, 1d);
public override double GetDelta(double t1, double t0)
=> MathUtils.Clamp(t1, 0d, 1d) - MathUtils.Clamp(t0, 0d, 1d);
}

// Fluent / 平滑
/// <summary>
/// 平滑开始。
/// </summary>
public class AniEaseInFluent(AniEasePower power = AniEasePower.Middle) : AniEase
{
private readonly AniEasePower _p = power;

Check notice on line 69 in UI/Animation/AniEase.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Replace with primary constructor parameter

Replace with primary constructor parameter

public override double GetValue(double t)
=> Math.Pow(MathUtils.Clamp(t, 0d, 1d), (double)_p);
}
/// <summary>
/// 平滑结束。
/// </summary>
public class AniEaseOutFluent(AniEasePower power = AniEasePower.Middle) : AniEase
{
private readonly AniEasePower _p = power;

Check notice on line 79 in UI/Animation/AniEase.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Replace with primary constructor parameter

Replace with primary constructor parameter

public override double GetValue(double t)
=> 1d - Math.Pow(MathUtils.Clamp(1d - t, 0d, 1d), (double)_p);
}
/// <summary>
/// 平滑开始与结束。
/// </summary>
public class AniEaseInoutFluent(AniEasePower power = AniEasePower.Middle, double middle = 0.5d) : AniEase
{
private AniEaseInout _ease = new(new AniEaseInFluent(power), new AniEaseOutFluent(power), middle);

Check notice on line 89 in UI/Animation/AniEase.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Field can be made readonly (private accessibility)

Field can be made readonly

public override double GetValue(double t) => _ease.GetValue(t);
}
/// <summary>
/// 以特定速度开始的平滑结束。
/// </summary>
public class AniEaseOutFluentWithInitial : AniEase
{
private readonly double _alpha; // (初速度 / 平均速度) – 1

/// <param name="initialPixelPerSecond">初速度,px/s</param>
/// <param name="totalSecond">总时长,s</param>
/// <param name="totalDistance">总路程,px</param>
public AniEaseOutFluentWithInitial(double initialPixelPerSecond, double totalSecond, double totalDistance)
{
double v0_norm = initialPixelPerSecond * totalSecond / totalDistance; // 归一化初速度

Check notice on line 105 in UI/Animation/AniEase.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use preferred 'var' style (for built-in types)

Use 'var' (built-in types)
_alpha = v0_norm - 1.0d;
if (_alpha < 0d)
_alpha = 0d; // 初速度小于平均速度时,退化为线性
}
public override double GetValue(double percent)
{
double p = MathUtils.Clamp(percent, 0d, 1d);

Check notice on line 112 in UI/Animation/AniEase.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use preferred 'var' style (for built-in types)

Use 'var' (built-in types)
if (_alpha == 0d)
return p; // 退化到线性
return (_alpha + 1d) * p / (1d + _alpha * p);
}
}

// Back / 回弹
/// <summary>
/// 回弹开始。有效时间为 1/3。
/// </summary>
public class AniEaseInBack(AniEasePower power = AniEasePower.Middle) : AniEase
{
private readonly double _p = 3d - (double)power * 0.5d;

public override double GetValue(double t)
{
t = MathUtils.Clamp(t, 0d, 1d);
return Math.Pow(t, _p) * Math.Cos(1.5d * Math.PI * (1d - t));
}
}
/// <summary>
/// 回弹结束。有效时间为 1/3。
/// </summary>
public class AniEaseOutBack(AniEasePower power = AniEasePower.Middle) : AniEase
{
private readonly double _p = 3d - (double)power * 0.5d;

public override double GetValue(double t)
{
t = MathUtils.Clamp(t, 0d, 1d);
return 1d - Math.Pow(1d - t, _p) * Math.Cos(1.5d * Math.PI * t);
}
}

// Car / 平滑-回弹
/// <summary>
/// 回弹开始,短平滑结束。
/// </summary>
public class AniEaseInCar(double middle = 0.7d, AniEasePower power = AniEasePower.Middle) : AniEase
{
private AniEaseInout _ease = new(new AniEaseInBack(power), new AniEaseOutFluent(power), middle);

Check notice on line 153 in UI/Animation/AniEase.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Field can be made readonly (private accessibility)

Field can be made readonly

public override double GetValue(double t)
=> _ease.GetValue(t);
}
/// <summary>
/// 短平滑开始,回弹结束。
/// </summary>
public class AniEaseOutCar(double middle = 0.3d, AniEasePower power = AniEasePower.Middle) : AniEase
{
private AniEaseInout _ease = new AniEaseInout(new AniEaseInFluent(power), new AniEaseOutBack(power), middle);

Check notice on line 163 in UI/Animation/AniEase.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use preferred style of 'new' expression when created type is evident

Redundant type specification

Check notice on line 163 in UI/Animation/AniEase.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Field can be made readonly (private accessibility)

Field can be made readonly

public override double GetValue(double t)
=> _ease.GetValue(t);
}

// Elastic / 弹簧
/// <summary>
/// 弹簧开始。约在 60% 到达最小值。
/// </summary>
public class AniEaseInElastic(AniEasePower power = AniEasePower.Middle) : AniEase
{
private readonly int _p = (int)power + 4; // 6~9

public override double GetValue(double t)
{
t = MathUtils.Clamp(t, 0d, 1d);
return Math.Pow(t, (_p - 1) * 0.25d) * Math.Cos((_p - 3.5d) * Math.PI * Math.Pow(1d - t, 1.5d));
}
}
/// <summary>
/// 弹簧结束。约在 40% 到达最大值。
/// </summary>
public class AniEaseOutElastic(AniEasePower power = AniEasePower.Middle) : AniEase
{
private readonly int _p = (int)power + 4;

public override double GetValue(double t)
{
t = 1d - MathUtils.Clamp(t, 0d, 1d);
return 1d - Math.Pow(t, (_p - 1) * 0.25d) * Math.Cos((_p - 3.5d) * Math.PI * Math.Pow(1d - t, 1.5d));
}
}
62 changes: 62 additions & 0 deletions UI/Animation/AniType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
namespace PCL.Core.UI.Animation;

/// <summary>
/// 动画基础种类。
/// </summary>
public enum AniType
{
/// <summary>
/// 单个Double的动画,包括位置、长宽、透明度等。这需要附属类型。
/// </summary>
/// <remarks></remarks>
Number,
/// <summary>
/// 颜色属性的动画。这需要附属类型。
/// </summary>
/// <remarks></remarks>
Color,
/// <summary>
/// 缩放控件大小。比起4个DoubleAnimation来说效率更高。
/// </summary>
/// <remarks></remarks>
Scale,
/// <summary>
/// 文字一个个出现。
/// </summary>
/// <remarks></remarks>
TextAppear,
/// <summary>
/// 执行代码。
/// </summary>
/// <remarks></remarks>
Code,
/// <summary>
/// 以 WPF 方式缩放控件。
/// </summary>
ScaleTransform,
/// <summary>
/// 以 WPF 方式旋转控件。
/// </summary>
RotateTransform
}

/// <summary>
/// 动画扩展种类。
/// </summary>
public enum AniTypeExt
{
X,
Y,
Width,
Height,
Opacity,
Value,
Radius,
BorderThickness,
StrokeThickness,
TranslateX,
TranslateY,
Double,
DoubleParam,
GridLengthWidth
}
Loading
Loading