/// <summary> /// Animates the tiles using the supplied entrance mode, Y direction, Z direction and duration. /// </summary> /// <param name="mode">The entrance mode (Enter or Exit).</param> /// <param name="yDirection">The direction of the animation in the vertical axis (TopToBottom or BottomToTop)</param> /// <param name="zDirection">The direction of the animation in the depth axis (FrontToBack or BackToFront)</param> /// <param name="duration">The duration of the animation for each tile</param> public void AnimateTiles(EnterMode mode, YDirection yDirection, ZDirection zDirection, TimeSpan duration) { // If the control has not been rendered, cancel the animation if (ActualWidth <= 0 || ActualHeight <= 0) { return; } // If the control it's empty, after position calculation is ran, cancel the animation CalculateElementPositions(); if (_positions == null || _positions.Count <= 0) { return; } // Calculates start and end heights double startHeight = 0; double endHeight = ActualHeight; startHeight = 0; endHeight = _scrollViewer.ViewportHeight; // Get the visible tiles for the current configuration // Tiles that are partially visible are also counted var visibleTiles = _positions.Where(x => x.Value.Y + x.Key.ActualHeight >= startHeight && x.Value.Y <= startHeight + endHeight); // No visible tiles, do nothing var visibleCount = visibleTiles.Count(); if (visibleCount <= 0) { return; } // The Y coordinate of the lowest element is useful // when we animate from bottom to top double lowestY = visibleTiles.Max(el => el.Value.Y); // Store the animations to group them in one Storyboard in the end var animations = new List<Timeline>(); var lastAnimations = new List<Timeline>(); foreach (var tilePosition in visibleTiles) { var currentTileAnimationDuration = duration; bool isTileSelected = tilePosition.Key.DataContext == SelectedItem; if (isTileSelected) { currentTileAnimationDuration = currentTileAnimationDuration.Multiply(0.8d); } var tile = tilePosition.Key; var position = tilePosition.Value; var projection = tile.Projection as PlaneProjection; double rotationFrom, rotationTo, opacityTo; // Reset all children's opacity regardless of their animations if (mode == EnterMode.Exit) { tile.Opacity = 1; opacityTo = 0; rotationFrom = 0; rotationTo = zDirection == ZDirection.BackToFront ? -90 : 90; } else { tile.Opacity = 0; opacityTo = 1; rotationFrom = zDirection == ZDirection.BackToFront ? -90 : 90; rotationTo = 0; } // Used to determine begin time - depends if we're moving from bottom or from top double relativeY; if (yDirection == YDirection.BottomToTop) { // The lowest element should have relativeY == 0 relativeY = lowestY - position.Y; } else { relativeY = position.Y; } var easing = new QuadraticEase() { EasingMode = EasingMode.EaseInOut }; var rotationAnimation = new DoubleAnimation { From = rotationFrom, To = rotationTo, EasingFunction = easing }; rotationAnimation.Duration = currentTileAnimationDuration; if (isTileSelected) { //animate tile as it's lower than all others (so it will be later even it's last in the list) rotationAnimation.BeginTime = currentTileAnimationDuration.Multiply(GetBeginTimeFactor(position.X, lowestY * 1.2d, mode)); } else { rotationAnimation.BeginTime = currentTileAnimationDuration.Multiply(GetBeginTimeFactor(position.X, relativeY, mode)); } rotationAnimation.SetTargetAndProperty(projection, PlaneProjection.RotationYProperty); var opacityAnimation = new DoubleAnimation { To = opacityTo, EasingFunction = easing }; // The opacity animation takes the last 60% of the rotation animation opacityAnimation.Duration = currentTileAnimationDuration.Multiply(0.6); opacityAnimation.BeginTime = rotationAnimation.BeginTime; if (mode == EnterMode.Exit) opacityAnimation.BeginTime += currentTileAnimationDuration - opacityAnimation.Duration.TimeSpan; opacityAnimation.SetTargetAndProperty(tile, UIElement.OpacityProperty); animations.Add(rotationAnimation); animations.Add(opacityAnimation); } animations.AddRange(lastAnimations); // Begin all animations var sb = new Storyboard(); foreach (var a in animations) { sb.Children.Add(a); } sb.SpeedRatio = SpeedRatio; sb.Completed += sb_Completed; sb.Begin(); }
private void AnimateTiles(EnterMode mode, XDirection xDirection, ZDirection zDirection, TimeSpan duration, Action callback) { // If the control has not been rendered or it's empty, cancel the animation if (this.ActualWidth <= 0 || this.ActualHeight <= 0 || this._positions == null || this._positions.Count <= 0) return; // Get the visible tiles for the current configuration // Tiles that are partially visible are also counted var visibleitems = this._positions.Where(x => x.Value.X + x.Key.ActualWidth >= 0 && x.Value.X <= this.ActualWidth && x.Value.Y + x.Key.ActualHeight >= 0 && x.Value.Y <= this.ActualHeight); // No visible tiles, do nothing if (visibleitems.Count() <= 0) return; // The Y coordinate of the lowest element is useful // when we animate from bottom to top double lowestX = visibleitems.Max(el => el.Value.X); // Store the animations to group them in one Storyboard in the end var animations = new List<Timeline>(); foreach (var tilePosition in visibleitems) { // To make syntax lighter var tile = tilePosition.Key; var position = tilePosition.Value; var projection = tile.Projection as PlaneProjection; double rotationFrom, rotationTo, opacityTo; // Reset all children's opacity regardless of their animations if (mode == EnterMode.Exit) { tile.Opacity = 1; opacityTo = 0; rotationFrom = 0; rotationTo = zDirection == ZDirection.BackToFront ? -90 : 90; } else { tile.Opacity = 0; opacityTo = 1; rotationFrom = zDirection == ZDirection.BackToFront ? -90 : 90; rotationTo = 0; } // Used to determine begin time - depends if we're moving from bottom or from top double relativeX; if (xDirection == XDirection.LeftToRight) { // The lowest element should have relativeY == 0 relativeX = lowestX - position.X; } else { relativeX = position.X; } var easing = new QuadraticEase() { EasingMode = EasingMode.EaseInOut }; var rotationAnimation = new DoubleAnimation { From = rotationFrom, To = rotationTo, EasingFunction = easing }; rotationAnimation.Duration = duration; rotationAnimation.BeginTime = duration.Multiply(this.GetBeginTimeFactorFavorite(relativeX, position.Y, mode)); rotationAnimation.SetTargetAndProperty(projection, PlaneProjection.RotationYProperty); var opacityAnimation = new DoubleAnimation { To = opacityTo, EasingFunction = easing }; // The opacity animation takes the last 60% of the rotation animation opacityAnimation.Duration = duration.Multiply(0.6); opacityAnimation.BeginTime = rotationAnimation.BeginTime; if (mode == EnterMode.Exit) opacityAnimation.BeginTime += duration - opacityAnimation.Duration.TimeSpan; opacityAnimation.SetTargetAndProperty(tile, UIElement.OpacityProperty); animations.Add(rotationAnimation); animations.Add(opacityAnimation); } // Begin all animations var sb = new Storyboard(); sb.Completed += (sender, args) => { if (callback != null) { callback(); } }; foreach (var a in animations) sb.Children.Add(a); sb.Begin(); }