增加单个轨道的时间因子和alpha混合
This commit is contained in:
@@ -21,6 +21,8 @@ namespace SpineViewer.Extensions
|
|||||||
foreach (var tr in self.AnimationState.IterTracks().Where(t => t is not null))
|
foreach (var tr in self.AnimationState.IterTracks().Where(t => t is not null))
|
||||||
{
|
{
|
||||||
var t = spineObject.AnimationState.SetAnimation(tr!.TrackIndex, tr.Animation, tr.Loop);
|
var t = spineObject.AnimationState.SetAnimation(tr!.TrackIndex, tr.Animation, tr.Loop);
|
||||||
|
t.TimeScale = tr.TimeScale;
|
||||||
|
t.Alpha = tr.Alpha;
|
||||||
if (keepTrackTime)
|
if (keepTrackTime)
|
||||||
t.TrackTime = tr.TrackTime;
|
t.TrackTime = tr.TrackTime;
|
||||||
}
|
}
|
||||||
@@ -38,7 +40,8 @@ namespace SpineViewer.Extensions
|
|||||||
foreach (var e in self.AnimationState.IterTracks())
|
foreach (var e in self.AnimationState.IterTracks())
|
||||||
{
|
{
|
||||||
if (e is not null)
|
if (e is not null)
|
||||||
self.AnimationState.SetAnimation(e.TrackIndex, e.Animation, e.Loop);
|
e.TrackTime = 0; // 直接重置时间能保留原本的 TrackEntry
|
||||||
|
//self.AnimationState.SetAnimation(e.TrackIndex, e.Animation, e.Loop);
|
||||||
}
|
}
|
||||||
self.Update(0);
|
self.Update(0);
|
||||||
}
|
}
|
||||||
@@ -65,7 +68,7 @@ namespace SpineViewer.Extensions
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 按给定的帧率获取所有轨道第一个条目动画全时长包围盒大小, 是一个耗时操作, 如果可能的话最好缓存结果
|
/// 按给定的帧率获取所有轨道第一个条目动画全时长包围盒大小, 是一个耗时操作, 如果可能的话最好缓存结果
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Rect GetAnimationBounds(this SpineObject self, float fps = 10)
|
public static Rect GetAnimationBounds(this SpineObject self, float fps = 30)
|
||||||
{
|
{
|
||||||
using var copy = self.Copy();
|
using var copy = self.Copy();
|
||||||
var bounds = copy.GetCurrentBounds();
|
var bounds = copy.GetCurrentBounds();
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ namespace SpineViewer.Models
|
|||||||
|
|
||||||
public event EventHandler<SlotAttachmentChangedEventArgs>? SlotAttachmentChanged;
|
public event EventHandler<SlotAttachmentChangedEventArgs>? SlotAttachmentChanged;
|
||||||
|
|
||||||
public event EventHandler<AnimationChangedEventArgs>? AnimationChanged;
|
public event EventHandler<TrackPropertyChangedEventArgs>? TrackPropertyChanged;
|
||||||
|
|
||||||
public SpineVersion Version => _spineObject.Version;
|
public SpineVersion Version => _spineObject.Version;
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ namespace SpineViewer.Models
|
|||||||
public float TimeScale
|
public float TimeScale
|
||||||
{
|
{
|
||||||
get { lock (_lock) return _spineObject.AnimationState.TimeScale; }
|
get { lock (_lock) return _spineObject.AnimationState.TimeScale; }
|
||||||
set { lock (_lock) SetProperty(_spineObject.AnimationState.TimeScale, Math.Clamp(value, -100f, 100f), v => _spineObject.AnimationState.TimeScale = v); }
|
set { lock (_lock) SetProperty(_spineObject.AnimationState.TimeScale, Math.Clamp(value, 0.01f, 100f), v => _spineObject.AnimationState.TimeScale = v); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -254,15 +254,59 @@ namespace SpineViewer.Models
|
|||||||
public void SetAnimation(int index, string name)
|
public void SetAnimation(int index, string name)
|
||||||
{
|
{
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
float lastTimeScale = 1f;
|
||||||
|
float lastAlpha = 1f;
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
if (_spineObject.Data.AnimationsByName.ContainsKey(name))
|
if (_spineObject.Data.AnimationsByName.ContainsKey(name))
|
||||||
{
|
{
|
||||||
_spineObject.AnimationState.SetAnimation(index, name, true);
|
// 需要记录之前的轨道属性值并还原
|
||||||
|
if (_spineObject.AnimationState.GetCurrent(index) is ITrackEntry entry)
|
||||||
|
{
|
||||||
|
lastTimeScale = entry.TimeScale;
|
||||||
|
lastAlpha = entry.Alpha;
|
||||||
|
}
|
||||||
|
entry = _spineObject.AnimationState.SetAnimation(index, name, true);
|
||||||
|
entry.TimeScale = lastTimeScale;
|
||||||
|
entry.Alpha = lastAlpha;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (changed) AnimationChanged?.Invoke(this, new(index, name));
|
if (changed) TrackPropertyChanged?.Invoke(this, new(index, nameof(TrackPropertyChangedEventArgs.AnimationName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetTrackTimeScale(int index)
|
||||||
|
{
|
||||||
|
lock (_lock) return _spineObject.AnimationState.GetCurrent(index)?.TimeScale ?? 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTrackTimeScale(int index, float scale)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_spineObject.AnimationState.GetCurrent(index) is ITrackEntry entry)
|
||||||
|
{
|
||||||
|
entry.TimeScale = Math.Clamp(scale, 0.01f, 100f);
|
||||||
|
TrackPropertyChanged?.Invoke(this, new(index, nameof(TrackPropertyChangedEventArgs.TimeScale)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetTrackAlpha(int index)
|
||||||
|
{
|
||||||
|
lock (_lock) return _spineObject.AnimationState.GetCurrent(index)?.Alpha ?? 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTrackAlpha(int index, float alpha)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_spineObject.AnimationState.GetCurrent(index) is ITrackEntry entry)
|
||||||
|
{
|
||||||
|
entry.Alpha = Math.Clamp(alpha, 0f, 1f);
|
||||||
|
TrackPropertyChanged?.Invoke(this, new(index, nameof(TrackPropertyChangedEventArgs.Alpha)));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] GetTrackIndices()
|
public int[] GetTrackIndices()
|
||||||
@@ -283,7 +327,7 @@ namespace SpineViewer.Models
|
|||||||
public void ClearTrack(int index)
|
public void ClearTrack(int index)
|
||||||
{
|
{
|
||||||
lock (_lock) _spineObject.AnimationState.ClearTrack(index);
|
lock (_lock) _spineObject.AnimationState.ClearTrack(index);
|
||||||
AnimationChanged?.Invoke(this, new(index, null));
|
TrackPropertyChanged?.Invoke(this, new(index, nameof(TrackPropertyChangedEventArgs.AnimationName)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetAnimationsTime()
|
public void ResetAnimationsTime()
|
||||||
@@ -458,7 +502,7 @@ namespace SpineViewer.Models
|
|||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(name))
|
if (!string.IsNullOrEmpty(name))
|
||||||
_spineObject.AnimationState.SetAnimation(trackIndex, name, true);
|
_spineObject.AnimationState.SetAnimation(trackIndex, name, true);
|
||||||
AnimationChanged?.Invoke(this, new(trackIndex, name));
|
TrackPropertyChanged?.Invoke(this, new(trackIndex, nameof(TrackPropertyChangedEventArgs.AnimationName)));
|
||||||
trackIndex++;
|
trackIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,10 +592,23 @@ namespace SpineViewer.Models
|
|||||||
public string? AttachmentName { get; } = attachmentName;
|
public string? AttachmentName { get; } = attachmentName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AnimationChangedEventArgs(int trackIndex, string? animationName) : EventArgs
|
/// <summary>
|
||||||
|
/// 模型动画轨道属性变化事件参数, 需要检索 <c><see cref="PropertyName"/></c> 来确定发生变化的属性是什么
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="trackIndex">发生属性变化的轨道索引</param>
|
||||||
|
/// <param name="propertyName">使用 <c>nameof</c> 设置发生改变的属性名</param>
|
||||||
|
public class TrackPropertyChangedEventArgs(int trackIndex, string propertyName) : EventArgs
|
||||||
{
|
{
|
||||||
public int TrackIndex { get; } = trackIndex;
|
public int TrackIndex { get; } = trackIndex;
|
||||||
public string? AnimationName { get; } = animationName;
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发生变化的属性名, 将会使用 <c>nameof</c> 设置为属性名称字符串
|
||||||
|
/// </summary>
|
||||||
|
public string PropertyName { get; } = propertyName;
|
||||||
|
|
||||||
|
public string? AnimationName { get; }
|
||||||
|
public float TimeScale { get; } = 1f;
|
||||||
|
public float Alpha { get; } = 1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SpineObjectLoadOptions
|
public class SpineObjectLoadOptions
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
<s:String x:Key="Str_UsePma">Premultiply Alpha</s:String>
|
<s:String x:Key="Str_UsePma">Premultiply Alpha</s:String>
|
||||||
<s:String x:Key="Str_Physics">Physics</s:String>
|
<s:String x:Key="Str_Physics">Physics</s:String>
|
||||||
<s:String x:Key="Str_TimeScale">Time Scale</s:String>
|
<s:String x:Key="Str_TimeScale">Time Scale</s:String>
|
||||||
<s:String x:Key="Str_TimeScaleTootltip">Time scale for a single model; a negative value plays the animation in reverse.</s:String>
|
<s:String x:Key="Str_TimeScaleTootltip">Time scale for a single model, must be positive.</s:String>
|
||||||
|
|
||||||
<s:String x:Key="Str_Transform">Transform</s:String>
|
<s:String x:Key="Str_Transform">Transform</s:String>
|
||||||
<s:String x:Key="Str_Scale">Scale</s:String>
|
<s:String x:Key="Str_Scale">Scale</s:String>
|
||||||
@@ -87,6 +87,10 @@
|
|||||||
<s:String x:Key="Str_AppendTrack">Add</s:String>
|
<s:String x:Key="Str_AppendTrack">Add</s:String>
|
||||||
<s:String x:Key="Str_InsertTrack">Insert</s:String>
|
<s:String x:Key="Str_InsertTrack">Insert</s:String>
|
||||||
<s:String x:Key="Str_ClearTrack">Clear</s:String>
|
<s:String x:Key="Str_ClearTrack">Clear</s:String>
|
||||||
|
<s:String x:Key="Str_TrackTimeScale">Time Scale</s:String>
|
||||||
|
<s:String x:Key="Str_TrackTimeScaleTooltip">Time scale for a single track, must be positive.</s:String>
|
||||||
|
<s:String x:Key="Str_TrackAlpha">Alpha Blending</s:String>
|
||||||
|
<s:String x:Key="Str_TrackAlphaTooltip">Value range: 0–1. Similar to image blending, controls how animations from higher-index tracks mix into lower-index tracks.</s:String>
|
||||||
|
|
||||||
<s:String x:Key="Str_Debug">Debug</s:String>
|
<s:String x:Key="Str_Debug">Debug</s:String>
|
||||||
<s:String x:Key="Str_DebugTexture">Texture</s:String>
|
<s:String x:Key="Str_DebugTexture">Texture</s:String>
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
<s:String x:Key="Str_UsePma">プレマルチプライドアルファ</s:String>
|
<s:String x:Key="Str_UsePma">プレマルチプライドアルファ</s:String>
|
||||||
<s:String x:Key="Str_Physics">物理</s:String>
|
<s:String x:Key="Str_Physics">物理</s:String>
|
||||||
<s:String x:Key="Str_TimeScale">時間スケール</s:String>
|
<s:String x:Key="Str_TimeScale">時間スケール</s:String>
|
||||||
<s:String x:Key="Str_TimeScaleTootltip">単一モデルの時間スケール。負の値にするとアニメーションを逆再生します。</s:String>
|
<s:String x:Key="Str_TimeScaleTootltip">単一モデルの時間スケール。正の値のみ指定可能です。</s:String>
|
||||||
|
|
||||||
<s:String x:Key="Str_Transform">変換</s:String>
|
<s:String x:Key="Str_Transform">変換</s:String>
|
||||||
<s:String x:Key="Str_Scale">スケール</s:String>
|
<s:String x:Key="Str_Scale">スケール</s:String>
|
||||||
@@ -87,6 +87,10 @@
|
|||||||
<s:String x:Key="Str_AppendTrack">追加</s:String>
|
<s:String x:Key="Str_AppendTrack">追加</s:String>
|
||||||
<s:String x:Key="Str_InsertTrack">挿入</s:String>
|
<s:String x:Key="Str_InsertTrack">挿入</s:String>
|
||||||
<s:String x:Key="Str_ClearTrack">削除</s:String>
|
<s:String x:Key="Str_ClearTrack">削除</s:String>
|
||||||
|
<s:String x:Key="Str_TrackTimeScale">時間スケール</s:String>
|
||||||
|
<s:String x:Key="Str_TrackTimeScaleTooltip">単一トラックの時間スケール。正の値のみ指定可能です。</s:String>
|
||||||
|
<s:String x:Key="Str_TrackAlpha">アルファ合成</s:String>
|
||||||
|
<s:String x:Key="Str_TrackAlphaTooltip">値の範囲:0~1。画像の合成と同様に、高インデックストラックのアニメーションが低インデックストラックにどの程度混合されるかを制御します。</s:String>
|
||||||
|
|
||||||
<s:String x:Key="Str_Debug">デバッグ</s:String>
|
<s:String x:Key="Str_Debug">デバッグ</s:String>
|
||||||
<s:String x:Key="Str_DebugTexture">テクスチャ</s:String>
|
<s:String x:Key="Str_DebugTexture">テクスチャ</s:String>
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
<s:String x:Key="Str_UsePma">预乘Alpha通道</s:String>
|
<s:String x:Key="Str_UsePma">预乘Alpha通道</s:String>
|
||||||
<s:String x:Key="Str_Physics">物理</s:String>
|
<s:String x:Key="Str_Physics">物理</s:String>
|
||||||
<s:String x:Key="Str_TimeScale">时间因子</s:String>
|
<s:String x:Key="Str_TimeScale">时间因子</s:String>
|
||||||
<s:String x:Key="Str_TimeScaleTootltip">单个模型的时间因子,取负数时可以倒放动画</s:String>
|
<s:String x:Key="Str_TimeScaleTootltip">单个模型的时间因子,只能取正数</s:String>
|
||||||
|
|
||||||
<s:String x:Key="Str_Transform">变换</s:String>
|
<s:String x:Key="Str_Transform">变换</s:String>
|
||||||
<s:String x:Key="Str_Scale">缩放</s:String>
|
<s:String x:Key="Str_Scale">缩放</s:String>
|
||||||
@@ -87,6 +87,10 @@
|
|||||||
<s:String x:Key="Str_AppendTrack">添加</s:String>
|
<s:String x:Key="Str_AppendTrack">添加</s:String>
|
||||||
<s:String x:Key="Str_InsertTrack">插入</s:String>
|
<s:String x:Key="Str_InsertTrack">插入</s:String>
|
||||||
<s:String x:Key="Str_ClearTrack">删除</s:String>
|
<s:String x:Key="Str_ClearTrack">删除</s:String>
|
||||||
|
<s:String x:Key="Str_TrackTimeScale">时间因子</s:String>
|
||||||
|
<s:String x:Key="Str_TrackTimeScaleTooltip">单个轨道的时间因子,只能取正数</s:String>
|
||||||
|
<s:String x:Key="Str_TrackAlpha">Alpha 混合</s:String>
|
||||||
|
<s:String x:Key="Str_TrackAlphaTooltip">取值范围 0-1,与图像混合类似,可以控制高索引轨道在低索引轨道中的动画混合比例</s:String>
|
||||||
|
|
||||||
<s:String x:Key="Str_Debug">调试</s:String>
|
<s:String x:Key="Str_Debug">调试</s:String>
|
||||||
<s:String x:Key="Str_DebugTexture">Texture</s:String>
|
<s:String x:Key="Str_DebugTexture">Texture</s:String>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
foreach (var obj in _selectedObjects)
|
foreach (var obj in _selectedObjects)
|
||||||
{
|
{
|
||||||
obj.PropertyChanged -= SingleModel_PropertyChanged;
|
obj.PropertyChanged -= SingleModel_PropertyChanged;
|
||||||
obj.AnimationChanged -= SingleModel_AnimationChanged;
|
obj.TrackPropertyChanged -= SingleModel_TrackPropChanged;
|
||||||
}
|
}
|
||||||
_skins.Clear();
|
_skins.Clear();
|
||||||
_slots.Clear();
|
_slots.Clear();
|
||||||
@@ -44,7 +44,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
foreach (var obj in _selectedObjects)
|
foreach (var obj in _selectedObjects)
|
||||||
{
|
{
|
||||||
obj.PropertyChanged += SingleModel_PropertyChanged;
|
obj.PropertyChanged += SingleModel_PropertyChanged;
|
||||||
obj.AnimationChanged += SingleModel_AnimationChanged;
|
obj.TrackPropertyChanged += SingleModel_TrackPropChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<string> commonSkinNames = _selectedObjects[0].Skins;
|
IEnumerable<string> commonSkinNames = _selectedObjects[0].Skins;
|
||||||
@@ -658,24 +658,24 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 监听单个模型动画轨道发生变化, 则重建聚合后的动画列表
|
/// 监听单个模型动画轨道发生变化, 则重建聚合后的动画列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sender"></param>
|
private void SingleModel_TrackPropChanged(object? sender, TrackPropertyChangedEventArgs e)
|
||||||
/// <param name="e"></param>
|
|
||||||
private void SingleModel_AnimationChanged(object? sender, AnimationChangedEventArgs e)
|
|
||||||
{
|
{
|
||||||
// XXX: 这里应该有更好的实现, 当 e.AnimationName == null 的时候代表删除轨道需要重新构建列表
|
if (e.PropertyName == nameof(TrackPropertyChangedEventArgs.AnimationName))
|
||||||
// 但是目前无法识别是否增加了轨道, 因此总是重建列表
|
{
|
||||||
|
// XXX: 这里应该有更好的实现, 当 e.AnimationName == null 的时候代表删除轨道需要重新构建列表
|
||||||
// 由于某些原因, 直接使用 Clear 会和 UI 逻辑冲突产生报错, 因此需要放到 Dispatcher 里延迟执行
|
// 但是目前无法识别是否增加了轨道, 因此总是重建列表
|
||||||
Application.Current.Dispatcher.BeginInvoke(
|
|
||||||
() =>
|
|
||||||
{
|
|
||||||
_animationTracks.Clear();
|
|
||||||
IEnumerable<int> commonTrackIndices = _selectedObjects[0].GetTrackIndices();
|
|
||||||
foreach (var obj in _selectedObjects.Skip(1)) commonTrackIndices = commonTrackIndices.Intersect(obj.GetTrackIndices());
|
|
||||||
foreach (var idx in commonTrackIndices) _animationTracks.Add(new(idx, _selectedObjects));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
// 由于某些原因, 直接使用 Clear 会和 UI 逻辑冲突产生报错, 因此需要放到 Dispatcher 里延迟执行
|
||||||
|
Application.Current.Dispatcher.BeginInvoke(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
_animationTracks.Clear();
|
||||||
|
IEnumerable<int> commonTrackIndices = _selectedObjects[0].GetTrackIndices();
|
||||||
|
foreach (var obj in _selectedObjects.Skip(1)) commonTrackIndices = commonTrackIndices.Intersect(obj.GetTrackIndices());
|
||||||
|
foreach (var idx in commonTrackIndices) _animationTracks.Add(new(idx, _selectedObjects));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkinViewModel : ObservableObject
|
public class SkinViewModel : ObservableObject
|
||||||
@@ -848,10 +848,10 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
// 使用弱引用, 则此 ViewModel 被释放时无需显式退订事件
|
// 使用弱引用, 则此 ViewModel 被释放时无需显式退订事件
|
||||||
foreach (var sp in _spines)
|
foreach (var sp in _spines)
|
||||||
{
|
{
|
||||||
WeakEventManager<SpineObjectModel, AnimationChangedEventArgs>.AddHandler(
|
WeakEventManager<SpineObjectModel, TrackPropertyChangedEventArgs>.AddHandler(
|
||||||
sp,
|
sp,
|
||||||
nameof(sp.AnimationChanged),
|
nameof(sp.TrackPropertyChanged),
|
||||||
SingleModel_AnimationChanged
|
SingleModel_TrackPropChanged
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -860,6 +860,24 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
|
|
||||||
public int TrackIndex => _trackIndex;
|
public int TrackIndex => _trackIndex;
|
||||||
|
|
||||||
|
public float? AnimationDuration
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_spines.Length <= 0) return null;
|
||||||
|
var ani = _spines[0].GetAnimation(_trackIndex);
|
||||||
|
if (ani is null) return null;
|
||||||
|
var val = _spines[0].GetAnimationDuration(ani);
|
||||||
|
foreach (var sp in _spines.Skip(1))
|
||||||
|
{
|
||||||
|
var a = sp.GetAnimation(_trackIndex);
|
||||||
|
if (a is null) return null;
|
||||||
|
if (sp.GetAnimationDuration(a) != val) return null;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string? AnimationName
|
public string? AnimationName
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -881,27 +899,54 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public float? AnimationDuration
|
public float? TrackTimeScale
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
// XXX: 空轨道和多选不相同都会返回 null
|
||||||
if (_spines.Length <= 0) return null;
|
if (_spines.Length <= 0) return null;
|
||||||
var ani = _spines[0].GetAnimation(_trackIndex);
|
var val = _spines[0].GetTrackTimeScale(_trackIndex);
|
||||||
if (ani is null) return null;
|
if (_spines.Skip(1).Any(it => it.GetTrackTimeScale(_trackIndex) != val)) return null;
|
||||||
var val = _spines[0].GetAnimationDuration(ani);
|
|
||||||
foreach (var sp in _spines.Skip(1))
|
|
||||||
{
|
|
||||||
var a = sp.GetAnimation(_trackIndex);
|
|
||||||
if (a is null) return null;
|
|
||||||
if (sp.GetAnimationDuration(a) != val) return null;
|
|
||||||
}
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_spines.Length <= 0) return;
|
||||||
|
if (value is null) return;
|
||||||
|
foreach (var sp in _spines) sp.SetTrackTimeScale(_trackIndex, (float)value);
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SingleModel_AnimationChanged(object? sender, AnimationChangedEventArgs e)
|
public float? TrackAlpha
|
||||||
{
|
{
|
||||||
if (e.TrackIndex == _trackIndex) OnPropertyChanged(nameof(AnimationName));
|
get
|
||||||
|
{
|
||||||
|
// XXX: 空轨道和多选不相同都会返回 null
|
||||||
|
if (_spines.Length <= 0) return null;
|
||||||
|
var val = _spines[0].GetTrackAlpha(_trackIndex);
|
||||||
|
if (_spines.Skip(1).Any(it => it.GetTrackAlpha(_trackIndex) != val)) return null;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_spines.Length <= 0) return;
|
||||||
|
if (value is null) return;
|
||||||
|
foreach (var sp in _spines) sp.SetTrackAlpha(_trackIndex, (float)value);
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SingleModel_TrackPropChanged(object? sender, TrackPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.TrackIndex == _trackIndex)
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(TrackPropertyChangedEventArgs.AnimationName)) OnPropertyChanged(nameof(AnimationName));
|
||||||
|
else if (e.PropertyName == nameof(TrackPropertyChangedEventArgs.TimeScale)) OnPropertyChanged(nameof(TrackTimeScale));
|
||||||
|
else if (e.PropertyName == nameof(TrackPropertyChangedEventArgs.Alpha)) OnPropertyChanged(nameof(TrackAlpha));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -472,15 +472,38 @@
|
|||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Col0"/>
|
<ColumnDefinition Width="Auto" SharedSizeGroup="ColTrackIdx"/>
|
||||||
|
<ColumnDefinition Width="Auto" SharedSizeGroup="ColAniTime"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Col2"/>
|
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Label Grid.Column="0" Content="{Binding TrackIndex}" HorizontalContentAlignment="Left" Background="#bfffffff"/>
|
|
||||||
<ComboBox Grid.Column="1" SelectedValue="{Binding AnimationName}" ItemsSource="{Binding AnimationNames}"/>
|
<Label Grid.Column="0" Content="{Binding TrackIndex}" HorizontalContentAlignment="Left" VerticalAlignment="Top" Background="#bfffffff"/>
|
||||||
<Label Grid.Column="2"
|
<Label Grid.Column="1" Content="{Binding AnimationDuration}" VerticalAlignment="Top" ContentStringFormat="{}{0:F3} s"/>
|
||||||
Content="{Binding AnimationDuration}"
|
|
||||||
ContentStringFormat="{}{0:F3} s"/>
|
<Expander Grid.Column="2" HorizontalContentAlignment="Stretch">
|
||||||
|
<Expander.Header>
|
||||||
|
<!-- hc 的模板自带左侧 10 的 padding, 此处用 -10 的 margin 来抵消去除 -->
|
||||||
|
<ComboBox Margin="-10 0 0 0" Grid.Column="2" SelectedValue="{Binding AnimationName}" ItemsSource="{Binding AnimationNames}"/>
|
||||||
|
</Expander.Header>
|
||||||
|
<Grid Margin="1 0 0 0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- 时间因子 -->
|
||||||
|
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_TrackTimeScale}" ToolTip="{DynamicResource Str_TrackTimeScaleTooltip}"/>
|
||||||
|
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding TrackTimeScale, StringFormat='{}{0:F3}'}" ToolTip="{DynamicResource Str_TrackTimeScaleTooltip}"/>
|
||||||
|
|
||||||
|
<!-- Alpha 混合 -->
|
||||||
|
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_TrackAlpha}" ToolTip="{DynamicResource Str_TrackAlphaTooltip}"/>
|
||||||
|
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding TrackAlpha, StringFormat='{}{0:F3}'}" ToolTip="{DynamicResource Str_TrackAlphaTooltip}"/>
|
||||||
|
</Grid>
|
||||||
|
</Expander>
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
@@ -735,7 +758,7 @@
|
|||||||
<ToggleButton Grid.Row="7" Grid.Column="1" IsChecked="{Binding FlipY}"/>
|
<ToggleButton Grid.Row="7" Grid.Column="1" IsChecked="{Binding FlipY}"/>
|
||||||
|
|
||||||
<Separator Grid.Row="8" Grid.Column="0" Grid.ColumnSpan="2" Margin="0 5"/>
|
<Separator Grid.Row="8" Grid.Column="0" Grid.ColumnSpan="2" Margin="0 5"/>
|
||||||
|
|
||||||
<!-- 最大帧率 -->
|
<!-- 最大帧率 -->
|
||||||
<Label Grid.Row="9" Grid.Column="0" Content="{DynamicResource Str_MaxFps}" ToolTip="{DynamicResource Str_MaxFpsTooltip}"/>
|
<Label Grid.Row="9" Grid.Column="0" Content="{DynamicResource Str_MaxFps}" ToolTip="{DynamicResource Str_MaxFpsTooltip}"/>
|
||||||
<TextBox Grid.Row="9" Grid.Column="1" Text="{Binding MaxFps}" ToolTip="{DynamicResource Str_MaxFpsTooltip}"/>
|
<TextBox Grid.Row="9" Grid.Column="1" Text="{Binding MaxFps}" ToolTip="{DynamicResource Str_MaxFpsTooltip}"/>
|
||||||
@@ -751,7 +774,7 @@
|
|||||||
<!-- 背景颜色 -->
|
<!-- 背景颜色 -->
|
||||||
<Label Grid.Row="12" Grid.Column="0" Content="{DynamicResource Str_BackgroundColor}" ToolTip="#AARRGGBB"/>
|
<Label Grid.Row="12" Grid.Column="0" Content="{DynamicResource Str_BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||||
<TextBox Grid.Row="12" Grid.Column="1" Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/>
|
<TextBox Grid.Row="12" Grid.Column="1" Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||||
|
|
||||||
<!-- 背景图案 -->
|
<!-- 背景图案 -->
|
||||||
<!-- 背景图案模式 -->
|
<!-- 背景图案模式 -->
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
Reference in New Issue
Block a user