csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
127 lines
3.8 KiB
127 lines
3.8 KiB
using System;
|
|
using System.Runtime.ExceptionServices;
|
|
using Avalonia.Reactive;
|
|
using Avalonia.Utilities;
|
|
|
|
namespace Avalonia.Animation
|
|
{
|
|
/// <summary>
|
|
/// Handles the timing and lifetime of a <see cref="Transition{T}"/>.
|
|
/// </summary>
|
|
internal class TransitionInstance : SingleSubscriberObservableBase<double>, IObserver<TimeSpan>
|
|
{
|
|
private IDisposable? _timerSubscription;
|
|
private TimeSpan _delay;
|
|
private TimeSpan _duration;
|
|
private readonly IClock _baseClock;
|
|
private TransitionClock? _clock;
|
|
|
|
public TransitionInstance(IClock clock, TimeSpan delay, TimeSpan duration)
|
|
{
|
|
clock = clock ?? throw new ArgumentNullException(nameof(clock));
|
|
|
|
_delay = delay;
|
|
_duration = duration;
|
|
_baseClock = clock;
|
|
}
|
|
|
|
private void TimerTick(TimeSpan t)
|
|
{
|
|
|
|
// [<------------- normalizedTotalDur ------------------>]
|
|
// [<---- Delay ---->][<---------- Duration ------------>]
|
|
// ^- normalizedDelayEnd
|
|
// [<---- normalizedInterpVal --->]
|
|
|
|
var normalizedInterpVal = 1d;
|
|
|
|
if (!MathUtilities.AreClose(_duration.TotalSeconds, 0d))
|
|
{
|
|
var normalizedTotalDur = _delay + _duration;
|
|
var normalizedDelayEnd = _delay.TotalSeconds / normalizedTotalDur.TotalSeconds;
|
|
var normalizedPresentationTime = t.TotalSeconds / normalizedTotalDur.TotalSeconds;
|
|
|
|
if (normalizedPresentationTime < normalizedDelayEnd
|
|
|| MathUtilities.AreClose(normalizedPresentationTime, normalizedDelayEnd))
|
|
{
|
|
normalizedInterpVal = 0d;
|
|
}
|
|
else
|
|
{
|
|
normalizedInterpVal = (t.TotalSeconds - _delay.TotalSeconds) / _duration.TotalSeconds;
|
|
}
|
|
}
|
|
|
|
// Clamp interpolation value.
|
|
if (normalizedInterpVal >= 1d || normalizedInterpVal < 0d)
|
|
{
|
|
PublishNext(1d);
|
|
PublishCompleted();
|
|
}
|
|
else
|
|
{
|
|
PublishNext(normalizedInterpVal);
|
|
}
|
|
}
|
|
|
|
protected override void Unsubscribed()
|
|
{
|
|
_timerSubscription?.Dispose();
|
|
_clock!.PlayState = PlayState.Stop;
|
|
}
|
|
|
|
protected override void Subscribed()
|
|
{
|
|
_clock = new TransitionClock(_baseClock);
|
|
_timerSubscription = _clock.Subscribe(this);
|
|
PublishNext(0.0d);
|
|
}
|
|
|
|
void IObserver<TimeSpan>.OnCompleted()
|
|
{
|
|
PublishCompleted();
|
|
}
|
|
|
|
void IObserver<TimeSpan>.OnError(Exception error)
|
|
{
|
|
PublishError(error);
|
|
}
|
|
|
|
void IObserver<TimeSpan>.OnNext(TimeSpan value)
|
|
{
|
|
TimerTick(value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// TODO: This clock is still fairly expensive due to <see cref="ClockBase"/> implementation.
|
|
/// </summary>
|
|
private sealed class TransitionClock : ClockBase, IObserver<TimeSpan>
|
|
{
|
|
private readonly IDisposable _parentSubscription;
|
|
|
|
public TransitionClock(IClock parent)
|
|
{
|
|
_parentSubscription = parent.Subscribe(this);
|
|
}
|
|
|
|
protected override void Stop()
|
|
{
|
|
_parentSubscription.Dispose();
|
|
}
|
|
|
|
void IObserver<TimeSpan>.OnNext(TimeSpan value)
|
|
{
|
|
Pulse(value);
|
|
}
|
|
|
|
void IObserver<TimeSpan>.OnCompleted()
|
|
{
|
|
}
|
|
|
|
void IObserver<TimeSpan>.OnError(Exception error)
|
|
{
|
|
ExceptionDispatchInfo.Capture(error).Throw();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|