您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

524 行
19 KiB

using System;
using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.physics;
using Unity.UIWidgets.ui;
using Ticker = Unity.UIWidgets.scheduler.Ticker;
using TickerFuture = Unity.UIWidgets.scheduler.TickerFuture;
using TickerProvider = Unity.UIWidgets.scheduler.TickerProvider;
namespace Unity.UIWidgets.animation {
enum _AnimationDirection {
forward,
reverse,
}
public enum AnimationBehavior {
normal,
preserve,
}
public class AnimationController :
AnimationLocalStatusListenersMixinAnimationLocalListenersMixinAnimationEagerListenerMixinAnimation<float> {
public AnimationController(
float? value = null,
TimeSpan? duration = null,
TimeSpan? reverseDuration = null,
string debugLabel = null,
float lowerBound = 0.0f,
float upperBound = 1.0f,
AnimationBehavior animationBehavior = AnimationBehavior.normal,
TickerProvider vsync = null
) {
D.assert(upperBound >= lowerBound);
D.assert(vsync != null);
_direction = _AnimationDirection.forward;
this.duration = duration;
this.reverseDuration = reverseDuration;
this.debugLabel = debugLabel;
this.lowerBound = lowerBound;
this.upperBound = upperBound;
this.animationBehavior = animationBehavior;
_ticker = vsync.createTicker(_tick);
_internalSetValue(value ?? lowerBound);
}
AnimationController(
float value = 0.0f,
TimeSpan? duration = null,
TimeSpan? reverseDuration = null,
string debugLabel = null,
AnimationBehavior? animationBehavior = null,
TickerProvider vsync = null
) {
D.assert(vsync != null);
D.assert(animationBehavior != null);
lowerBound = float.NegativeInfinity;
upperBound = float.PositiveInfinity;
_direction = _AnimationDirection.forward;
this.duration = duration;
this.reverseDuration = reverseDuration;
this.debugLabel = debugLabel;
this.animationBehavior = animationBehavior.Value;
_ticker = vsync.createTicker(_tick);
_internalSetValue(value);
}
public static AnimationController unbounded(
float value = 0.0f,
TimeSpan? duration = null,
TimeSpan? reverseDuration = null,
string debugLabel = null,
TickerProvider vsync = null,
AnimationBehavior animationBehavior = AnimationBehavior.preserve
) {
return new AnimationController(value, duration, reverseDuration, debugLabel, animationBehavior, vsync);
}
public readonly float lowerBound;
public readonly float upperBound;
public readonly AnimationBehavior animationBehavior;
public readonly string debugLabel;
public Animation<float> view {
get { return this; }
}
public TimeSpan? duration;
public TimeSpan? reverseDuration;
Ticker _ticker;
public void resync(TickerProvider vsync) {
Ticker oldTicker = _ticker;
_ticker = vsync.createTicker(_tick);
_ticker.absorbTicker(oldTicker);
}
Simulation _simulation;
public override float value {
get { return _value; }
}
float _value;
public void setValue(float newValue) {
stop();
_internalSetValue(newValue);
notifyListeners();
_checkStatusChanged();
}
public void reset() {
setValue(lowerBound);
}
public float velocity {
get {
if (!isAnimating) {
return 0.0f;
}
return _simulation.dx((float) lastElapsedDuration.Value.Ticks / TimeSpan.TicksPerSecond);
}
}
void _internalSetValue(float newValue) {
_value = newValue.clamp(lowerBound, upperBound);
if (foundation_.FloatEqual(_value, lowerBound)) {
_status = AnimationStatus.dismissed;
}
else if (foundation_.FloatEqual(_value, upperBound)) {
_status = AnimationStatus.completed;
}
else {
_status = (_direction == _AnimationDirection.forward)
? AnimationStatus.forward
: AnimationStatus.reverse;
}
}
TimeSpan? lastElapsedDuration {
get { return _lastElapsedDuration; }
}
TimeSpan? _lastElapsedDuration;
public bool isAnimating {
get { return _ticker != null && _ticker.isActive; }
}
_AnimationDirection _direction;
public override AnimationStatus status {
get { return _status; }
}
AnimationStatus _status;
public TickerFuture forward(float? from = null) {
D.assert(() => {
if (duration == null) {
throw new UIWidgetsError(
"AnimationController.forward() called with no default duration.\n" +
"The \"duration\" property should be set, either in the constructor or later, before " +
"calling the forward() function."
);
}
return true;
});
D.assert(
_ticker != null,
() => "AnimationController.forward() called after AnimationController.dispose()\n" +
"AnimationController methods should not be used after calling dispose."
);
_direction = _AnimationDirection.forward;
if (from != null) {
setValue(from.Value);
}
return _animateToInternal(upperBound);
}
public TickerFuture reverse(float? from = null) {
D.assert(() => {
if (duration == null && reverseDuration == null) {
throw new UIWidgetsError(
"AnimationController.reverse() called with no default duration or reverseDuration.\n" +
"The \"duration\" or \"reverseDuration\" property should be set, either in the constructor or later, before " +
"calling the reverse() function."
);
}
return true;
});
D.assert(
_ticker != null,
() => "AnimationController.reverse() called after AnimationController.dispose()\n" +
"AnimationController methods should not be used after calling dispose."
);
_direction = _AnimationDirection.reverse;
if (from != null) {
setValue(from.Value);
}
return _animateToInternal(lowerBound);
}
public TickerFuture animateTo(float target, TimeSpan? duration = null, Curve curve = null) {
D.assert(
_ticker != null,
() => "AnimationController.animateTo() called after AnimationController.dispose()\n" +
"AnimationController methods should not be used after calling dispose."
);
curve = curve ?? Curves.linear;
_direction = _AnimationDirection.forward;
return _animateToInternal(target, duration: duration, curve: curve);
}
public TickerFuture animateBack(float target, TimeSpan? duration, Curve curve = null) {
D.assert(
_ticker != null,
() => "AnimationController.animateBack() called after AnimationController.dispose()\n" +
"AnimationController methods should not be used after calling dispose."
);
curve = curve ?? Curves.linear;
_direction = _AnimationDirection.reverse;
return _animateToInternal(target, duration, curve);
}
TickerFuture _animateToInternal(float target, TimeSpan? duration = null, Curve curve = null) {
curve = curve ?? Curves.linear;
TimeSpan? simulationDuration = duration;
if (simulationDuration == null) {
D.assert(() => {
if ((this.duration == null && _direction == _AnimationDirection.reverse && reverseDuration == null) ||
(this.duration == null && _direction == _AnimationDirection.forward)) {
throw new UIWidgetsError(
"AnimationController.animateTo() called with no explicit Duration and no default duration or reverseDuration.\n" +
"Either the \"duration\" argument to the animateTo() method should be provided, or the " +
"\"duration\" and/or \"reverseDuration\" property should be set, either in the constructor or later, before " +
"calling the animateTo() function."
);
}
return true;
});
float range = upperBound - lowerBound;
float remainingFraction = range.isFinite() ? (target - _value).abs() / range : 1.0f;
TimeSpan directionDuration = (_direction == _AnimationDirection.reverse && reverseDuration != null)
? reverseDuration.Value
: this.duration.Value;
simulationDuration = TimeSpan.FromTicks((long) (directionDuration.Ticks * remainingFraction));
}
else if (target == value) {
simulationDuration = TimeSpan.Zero;
}
stop();
if (simulationDuration == TimeSpan.Zero) {
if (_value != target) {
_value = target.clamp(lowerBound, upperBound);
notifyListeners();
}
_status = (_direction == _AnimationDirection.forward)
? AnimationStatus.completed
: AnimationStatus.dismissed;
_checkStatusChanged();
return TickerFuture.complete();
}
D.assert(simulationDuration > TimeSpan.Zero);
D.assert(!isAnimating);
return _startSimulation(
new _InterpolationSimulation(_value, target, simulationDuration.Value, curve));
}
public TickerFuture repeat(float? min = null, float? max = null, bool reverse = false,
TimeSpan? period = null) {
min = min ?? lowerBound;
max = max ?? upperBound;
period = period ?? duration;
D.assert(() => {
if (period == null) {
throw new UIWidgetsError(
"AnimationController.repeat() called without an explicit period and with no default Duration.\n" +
"Either the \"period\" argument to the repeat() method should be provided, or the " +
"\"duration\" property should be set, either in the constructor or later, before " +
"calling the repeat() function."
);
}
return true;
});
D.assert(max >= min);
D.assert(max <= upperBound && min >= lowerBound);
stop();
return _startSimulation(new _RepeatingSimulation(_value, min.Value, max.Value, reverse, period.Value, _directionSetter));
}
void _directionSetter(_AnimationDirection direction) {
_direction = direction;
_status = (_direction == _AnimationDirection.forward) ? AnimationStatus.forward : AnimationStatus.reverse;
_checkStatusChanged();
}
public TickerFuture fling(float velocity = 1.0f) {
_direction = velocity < 0.0 ? _AnimationDirection.reverse : _AnimationDirection.forward;
float target = velocity < 0.0f
? lowerBound - _kFlingTolerance.distance
: upperBound + _kFlingTolerance.distance;
Simulation simulation = new SpringSimulation(_kFlingSpringDescription, value,
target, velocity);
simulation.tolerance = _kFlingTolerance;
stop();
return _startSimulation(simulation);
}
public TickerFuture animateWith(Simulation simulation) {
D.assert(
_ticker != null,
() => "AnimationController.animateWith() called after AnimationController.dispose()\n" +
"AnimationController methods should not be used after calling dispose."
);
stop();
_direction = _AnimationDirection.forward;
return _startSimulation(simulation);
}
TickerFuture _startSimulation(Simulation simulation) {
D.assert(simulation != null);
D.assert(!isAnimating);
_simulation = simulation;
_lastElapsedDuration = TimeSpan.Zero;
_value = simulation.x(0.0f).clamp(lowerBound, upperBound);
var result = _ticker.start();
_status = (_direction == _AnimationDirection.forward)
? AnimationStatus.forward
: AnimationStatus.reverse;
_checkStatusChanged();
return result;
}
public void stop(bool canceled = true) {
D.assert(
_ticker != null,
() => "AnimationController.stop() called after AnimationController.dispose()\n" +
"AnimationController methods should not be used after calling dispose."
);
_simulation = null;
_lastElapsedDuration = null;
_ticker.stop(canceled: canceled);
}
public override void dispose() {
D.assert(() => {
if (_ticker == null) {
throw new UIWidgetsError(new List<DiagnosticsNode>() {
new ErrorSummary("AnimationController.dispose() called more than once."),
new ErrorDescription($"A given {GetType()} cannot be disposed more than once.\n"),
new DiagnosticsProperty<AnimationController>(
$"The following {GetType()} object was disposed multiple times",
this,
style: DiagnosticsTreeStyle.errorProperty)
});
}
return true;
});
_ticker.Dispose();
_ticker = null;
base.dispose();
}
AnimationStatus _lastReportedStatus = AnimationStatus.dismissed;
void _checkStatusChanged() {
AnimationStatus newStatus = status;
if (_lastReportedStatus != newStatus) {
_lastReportedStatus = newStatus;
notifyStatusListeners(newStatus);
}
}
void _tick(TimeSpan elapsed) {
_lastElapsedDuration = elapsed;
float elapsedInSeconds = (float) elapsed.Ticks / TimeSpan.TicksPerSecond;
D.assert(elapsedInSeconds >= 0.0);
_value = _simulation.x(elapsedInSeconds).clamp(lowerBound, upperBound);
if (_simulation.isDone(elapsedInSeconds)) {
_status = (_direction == _AnimationDirection.forward)
? AnimationStatus.completed
: AnimationStatus.dismissed;
stop(canceled: false);
}
notifyListeners();
_checkStatusChanged();
}
public override string toStringDetails() {
string paused = isAnimating ? "" : "; paused";
string ticker = _ticker == null ? "; DISPOSED" : (_ticker.muted ? "; silenced" : "");
string label = debugLabel == null ? "" : "; for " + debugLabel;
string more = $"{base.toStringDetails()} {value:F3}";
return more + paused + ticker + label;
}
static readonly SpringDescription _kFlingSpringDescription = SpringDescription.withDampingRatio(
mass: 1.0f,
stiffness: 500.0f,
ratio: 1.0f
);
static readonly Tolerance _kFlingTolerance = new Tolerance(
velocity: float.PositiveInfinity,
distance: 0.01f
);
}
class _InterpolationSimulation : Simulation {
internal _InterpolationSimulation(float begin, float end, TimeSpan duration, Curve curve) {
_begin = begin;
_end = end;
_curve = curve;
D.assert(duration.Ticks > 0);
_durationInSeconds = (float) duration.Ticks / TimeSpan.TicksPerSecond;
}
readonly float _durationInSeconds;
readonly float _begin;
readonly float _end;
readonly Curve _curve;
public override float x(float timeInSeconds) {
float t = (timeInSeconds / _durationInSeconds).clamp(0.0f, 1.0f);
if (t == 0.0f) {
return _begin;
}
else if (t == 1.0f) {
return _end;
}
else {
return _begin + (_end - _begin) * _curve.transform(t);
}
}
public override float dx(float timeInSeconds) {
float epsilon = tolerance.time;
return (x(timeInSeconds + epsilon) - x(timeInSeconds - epsilon)) / (2 * epsilon);
}
public override bool isDone(float timeInSeconds) {
return timeInSeconds > _durationInSeconds;
}
}
delegate void _DirectionSetter(_AnimationDirection direction);
class _RepeatingSimulation : Simulation {
internal _RepeatingSimulation(float initialValue, float min, float max, bool reverse, TimeSpan period, _DirectionSetter directionSetter) {
_min = min;
_max = max;
_periodInSeconds = (float) period.Ticks / TimeSpan.TicksPerSecond;
_initialT =
(max == min) ? 0.0f : (initialValue / (max - min)) * (period.Ticks / TimeSpan.TicksPerSecond);
_reverse = reverse;
this.directionSetter = directionSetter;
D.assert(_periodInSeconds > 0.0f);
D.assert(_initialT >= 0.0f);
}
readonly float _min;
readonly float _max;
readonly float _periodInSeconds;
readonly bool _reverse;
readonly float _initialT;
readonly _DirectionSetter directionSetter;
public override float x(float timeInSeconds) {
D.assert(timeInSeconds >= 0.0f);
float totalTimeInSeconds = timeInSeconds + _initialT;
float t = (totalTimeInSeconds / _periodInSeconds) % 1.0f;
bool _isPlayingReverse = ((int) (totalTimeInSeconds / _periodInSeconds)) % 2 == 1;
if (_reverse && _isPlayingReverse) {
directionSetter(_AnimationDirection.reverse);
return MathUtils.lerpNullableFloat(_max, _min, t);
}
else {
directionSetter(_AnimationDirection.forward);
return MathUtils.lerpNullableFloat(_min, _max, t);
}
}
public override float dx(float timeInSeconds) {
return (_max - _min) / _periodInSeconds;
}
public override bool isDone(float timeInSeconds) {
return false;
}
}
}