/// <summary> /// Gets the cycle offset for a time value. /// </summary> /// <typeparam name="T">The type of animation value.</typeparam> /// <param name="time">The time value.</param> /// <param name="startTime">The start time.</param> /// <param name="endTime">The end time.</param> /// <param name="startValue">In: The animation value at <paramref name="startTime"/>.</param> /// <param name="endValue">In: The animation value at <paramref name="endTime"/>.</param> /// <param name="traits">The traits of the animation value.</param> /// <param name="loopBehavior">The post-loop behavior.</param> /// <param name="cycleOffset"> /// Out: The cycle offset. /// </param> /// <remarks> /// The cycle offset is <see cref="IAnimationValueTraits{T}.SetIdentity"/> if the /// <see cref="LoopBehavior"/> is unequal to <see cref="LoopBehavior.CycleOffset"/> or if the /// <paramref name="time"/> is in the regular cycle (between the first and the last key frame). /// </remarks> internal static void GetCycleOffset <T>(TimeSpan time, TimeSpan startTime, TimeSpan endTime, ref T startValue, ref T endValue, IAnimationValueTraits <T> traits, LoopBehavior loopBehavior, ref T cycleOffset) { Debug.Assert(endTime > startTime, "Invalid start and end time."); TimeSpan length = endTime - startTime; // Handle cycle offset. if (loopBehavior == LoopBehavior.CycleOffset && length != TimeSpan.Zero) { traits.Invert(ref startValue, ref startValue); traits.Add(ref startValue, ref endValue, ref cycleOffset); if (time < startTime) { long numberOfPeriods = (time.Ticks - endTime.Ticks) / length.Ticks; Debug.Assert(numberOfPeriods < 0, "Negative number of periods expected."); traits.Multiply(ref cycleOffset, (int)numberOfPeriods, ref cycleOffset); } else if (time > endTime) { long numberOfPeriods = (time.Ticks - startTime.Ticks) / length.Ticks; Debug.Assert(numberOfPeriods > 0, "Positive number of periods expected."); traits.Multiply(ref cycleOffset, (int)numberOfPeriods, ref cycleOffset); } else { traits.SetIdentity(ref cycleOffset); } } }
/// <summary> /// Animates the dependency value using an internally-managed clock. /// </summary> /// <param name="value">The animation's target value.</param> /// <param name="fn">The animation's easing function.</param> /// <param name="loopBehavior">A <see cref="LoopBehavior"/> value specifying the loop behavior of the animation.</param> /// <param name="duration">The animation's duration.</param> public void Animate(T value, EasingFunction fn, LoopBehavior loopBehavior, TimeSpan duration) { var clock = SimpleClockPool.Instance.Retrieve(this); var clockValue = clock.Value; clockValue.SetLoopBehavior(loopBehavior); clockValue.SetDuration(duration); clockValue.Start(); Animate(value, fn, clockValue); }
/// <summary> /// Initializes a new instance of the <see cref="TimelineClip"/> class for the given timeline. /// </summary> /// <param name="timeline">The timeline.</param> public TimelineClip(ITimeline timeline) { Timeline = timeline; FillBehavior = FillBehavior.Hold; TargetObject = null; ClipStart = null; ClipEnd = null; ClipOffset = TimeSpan.Zero; IsClipReversed = false; Delay = TimeSpan.Zero; Duration = null; Speed = 1.0f; LoopBehavior = LoopBehavior.Constant; }
/// <summary> /// Animates the specified dependency property to the specified target value using an internally-managed clock. /// </summary> /// <typeparam name="T">The type of value being animated.</typeparam> /// <param name="dp">A <see cref="DependencyProperty"/> instance which identifies the dependency property to animate.</param> /// <param name="value">The animation's target value.</param> /// <param name="fn">The animation's easing function.</param> /// <param name="loopBehavior">A <see cref="LoopBehavior"/> value specifying the loop behavior of the animation.</param> /// <param name="duration">The animation's duration.</param> public void Animate <T>(DependencyProperty dp, T value, EasingFunction fn, LoopBehavior loopBehavior, TimeSpan duration) { Contract.Require(dp, nameof(dp)); if (dp.IsReadOnly) { throw new InvalidOperationException(PresentationStrings.DependencyPropertyIsReadOnly.Format(dp.Name)); } if (!typeof(T).Equals(dp.PropertyType)) { throw new InvalidCastException(); } var wrapper = GetDependencyPropertyValue <T>(dp); wrapper.Animate(value, fn, loopBehavior, duration); }
/// <summary> /// Initializes a new instance of the <see cref="AnimationClip{T}"/> class for the given /// animation. /// </summary> /// <param name="animation">The original animation.</param> public AnimationClip(IAnimation <T> animation) { Animation = animation; FillBehavior = FillBehavior.Hold; IsAdditive = false; TargetObject = null; TargetProperty = null; ClipStart = null; ClipEnd = null; ClipOffset = TimeSpan.Zero; IsClipReversed = false; Delay = TimeSpan.Zero; Duration = null; Speed = 1.0f; LoopBehavior = LoopBehavior.Constant; }
/// <summary> /// Initializes a new instance of the <see cref="Storyboard"/> class. /// </summary> /// <param name="uv">The Ultraviolet context.</param> /// <param name="loopBehavior">The storyboard's loop behavior.</param> public Storyboard(UltravioletContext uv, LoopBehavior loopBehavior = LoopBehavior.None) { this.targets = new StoryboardTargetCollection(this); this.loopBehavior = loopBehavior; }
/// <summary> /// Initializes a new instance of the <see cref="UvssStoryboard"/> class. /// </summary> /// <param name="name">The storyboard's name.</param> /// <param name="loopBehavior">The storyboard's loop behavior.</param> /// <param name="targets">The storyboard's collection of targets.</param> internal UvssStoryboard(String name, LoopBehavior loopBehavior, UvssStoryboardTargetCollection targets) { this.name = name; this.loopBehavior = loopBehavior; this.targets = targets; }
/// <summary> /// Sets the clock's loop behavior. /// </summary> /// <param name="loopBehavior">The clock's loop behavior.</param> internal void SetLoopBehavior(LoopBehavior loopBehavior) { this.loopBehavior = loopBehavior; }
/// <summary> /// Initializes a new instance of the <see cref="SimpleClock"/> class. /// </summary> /// <param name="loopBehavior">The clock's loop behavior.</param> /// <param name="duration">The clock's duration.</param> public SimpleClock(LoopBehavior loopBehavior, TimeSpan duration) { this.loopBehavior = loopBehavior; this.duration = duration; }
/// <summary> /// Handles different loop behaviors by changing the given time value so that it lies between /// start and end time. /// </summary> /// <param name="time">The time value.</param> /// <param name="startTime">The start time.</param> /// <param name="endTime">The end time.</param> /// <param name="loopBehavior">The loop behavior.</param> /// <param name="loopedTime">The adjusted time value.</param> /// <returns> /// <see langword="true"/> if the current cycle requires an offset; otherwise, /// <see langword="false"/>. /// </returns> internal static bool LoopParameter(TimeSpan time, TimeSpan startTime, TimeSpan endTime, LoopBehavior loopBehavior, out TimeSpan loopedTime) { Debug.Assert(endTime >= startTime, "Invalid start and end time."); TimeSpan length = endTime - startTime; if (length == TimeSpan.Zero) { loopedTime = startTime; return(false); } if (time < startTime) { #region ----- Pre-loop ----- // Handle pre-loop. For some loop types we return immediately. For some // we adjust the time value. if (loopBehavior == LoopBehavior.Constant) { loopedTime = startTime; return(false); } long numberOfPeriods = (endTime.Ticks - time.Ticks) / length.Ticks; if (loopBehavior == LoopBehavior.Cycle || loopBehavior == LoopBehavior.CycleOffset) { loopedTime = new TimeSpan(time.Ticks + length.Ticks * numberOfPeriods); return(loopBehavior == LoopBehavior.CycleOffset); } Debug.Assert(loopBehavior == LoopBehavior.Oscillate); if (numberOfPeriods % 2 != 0) { // odd = mirrored loopedTime = new TimeSpan(startTime.Ticks + endTime.Ticks - (time.Ticks + length.Ticks * numberOfPeriods)); return(false); } // even = not mirrored loopedTime = new TimeSpan(time.Ticks + length.Ticks * numberOfPeriods); return(false); #endregion } if (time > endTime) { #region ----- Post-loop ----- // Handle post-loop. For some loop types we return immediately. For some // we adjust the time value. if (loopBehavior == LoopBehavior.Constant) { loopedTime = endTime; return(false); } long numberOfPeriods = (time.Ticks - startTime.Ticks) / length.Ticks; if (loopBehavior == LoopBehavior.Cycle || loopBehavior == LoopBehavior.CycleOffset) { loopedTime = new TimeSpan(time.Ticks - length.Ticks * numberOfPeriods); return(loopBehavior == LoopBehavior.CycleOffset); } Debug.Assert(loopBehavior == LoopBehavior.Oscillate); if (numberOfPeriods % 2 != 0) { // odd = mirrored loopedTime = new TimeSpan(endTime.Ticks - (time.Ticks - startTime.Ticks - length.Ticks * numberOfPeriods)); return(false); } // even = not mirrored loopedTime = new TimeSpan(time.Ticks - length.Ticks * numberOfPeriods); return(false); #endregion } loopedTime = time; return(false); }
/// <summary> /// Determines whether the given time value corresponds to a mirrored oscillation loop. /// </summary> /// <param name="time">The time value.</param> /// <param name="startTime">The start time.</param> /// <param name="endTime">The end time.</param> /// <param name="loopBehavior">The loop behavior.</param> /// <returns> /// <see langword="true"/> if the time value is in a mirrored oscillation loop; otherwise, /// <see langword="false"/>. /// </returns> internal static bool IsInMirroredOscillation(TimeSpan time, TimeSpan startTime, TimeSpan endTime, LoopBehavior loopBehavior) { Debug.Assert(endTime >= startTime, "Invalid start and end time."); TimeSpan length = endTime - startTime; if (length == TimeSpan.Zero) { return(false); } if (loopBehavior == LoopBehavior.Oscillate) { if (time < startTime) { return(((startTime.Ticks - time.Ticks) / length.Ticks) % 2 == 0); } if (time > endTime) { return(((time.Ticks - endTime.Ticks) / length.Ticks) % 2 == 0); } } return(false); }