private void AnimatedCanvasCtrl_OnDraw(ICanvasAnimatedControl sender, CanvasAnimatedDrawEventArgs args) { if (_animatedCompositionMask == null) { return; } _angle = (float)((_angle + 1) % 360); var radians = (float)((_angle * Math.PI) / 180); // Calculate the new geometry based on the angle var updatedGeometry = _outerGeometry.CombineWith(_combinedGeometry, Matrix3x2.CreateRotation(radians, new Vector2(_width / 2, _height / 2)), CanvasGeometryCombine.Exclude); // Update the geometry in the Composition Mask _animatedCompositionMask.Redraw(updatedGeometry); }
/// <summary> /// Handles the Arrange layout phase /// </summary> /// <param name="finalSize">Final Size of the control</param> /// <returns>Size</returns> protected override Size ArrangeOverride(Size finalSize) { if ((_compositor == null) || (_generator == null)) { return(base.ArrangeOverride(finalSize)); } if (Double.IsInfinity(finalSize.Width) || Double.IsInfinity(finalSize.Height) || Double.IsNaN(finalSize.Width) || Double.IsNaN(finalSize.Height)) { return(base.ArrangeOverride(finalSize)); } // Stop the animations and dispose the previous nodes // and their animations if (_isAnimationStarted) { for (var i = 0; i < _nodes.Count; i++) { _nodes[i].StopAnimation(AnimatedProperty); _animations[i].Dispose(); _animations[i] = null; _nodes[i].Dispose(); _nodes[i] = null; } _container.StopAnimation(AnimatedProperty); _containerAnimation.Dispose(); _containerAnimation = null; _container.Dispose(); _container = null; _animations.Clear(); _nodes.Clear(); _isAnimationStarted = false; } // Validate MaxNodes and ActiveNodes if ((MaxNodes <= 0) || (ActiveNodes <= 0)) { return(finalSize); } // Coerce ActiveNodes to MaxNodes if ActiveNodes > MaxNodes if (ActiveNodes > MaxNodes) { ActiveNodes = MaxNodes; } // Take the smallest of the width or height var sideLength = (float)Math.Min(finalSize.Width, finalSize.Height); // Size of the progress ring displayed _ringSize = new Vector2(sideLength, sideLength); var sideHalf = sideLength / 2f; // Radius of each node _nodeRadius = (float)NodeSizeFactor * sideHalf; // Size of each node _nodeSize = new Vector2(_nodeRadius * 2f, _nodeRadius * 2f); // Radius of the node _ringRadius = sideHalf - _nodeRadius; // Each animation will consist of '_maxFrameBlocks' number of // FrameBlocks. Each FrameBlock will consist of keyframes which allow // the element being animated to move to the next slot and wait // at that slot until all other elements have moved to their next slot. // Each FrameBlock (except the last one) will have '_maxFramesPerBlock' // number of keyframes. The last keyframe in the last FrameBlock // will always be (1f, "this.StartingValue + 360") // Total number of FrameBlocks in each animation _maxFrameBlocks = ActiveNodes; // Total keyframes in each FrameBlock _maxFramesPerBlock = ActiveNodes + 1; // Total keyframes in each animation _maxKeyFrames = _maxFrameBlocks * _maxFramesPerBlock; // Normalized Progress Key unit value for each keyframe _keyFrameSlice = 1f / _maxKeyFrames; // ======================================================================== // NOTE: // gamma * maxNodes = 360 // gamma = alpha + beta // beta = 2 * Asin(nodeRadius / ringRadius) * (180 / Math.PI) // invisibleNodes = MaxNodes - ActiveNodes // phi = (invisibleNodes * gamma) // theta = phi - beta // ======================================================================== // gamma is the angle between adjacent nodes when maxNodes number of nodes are arranged in a circle _gamma = 360f / MaxNodes; // beta is the angle a node must travel after hitting the adjacent node _beta = 2f * (float)(Math.Asin(_nodeRadius / _ringRadius) * (180f / Math.PI)); // alpha is the smallest angle a node must travel before hitting the adjacent node _alpha = _gamma - _beta; // phi is the angle occupied by (MaxNodes - ActiveNodes) number of nodes _phi = (MaxNodes - ActiveNodes + 1) * _gamma; // theta is the largest angle a node must travel before hitting the adjacent node _theta = _phi - _beta; // Create the Animations _animations = CreateAnimations(); // Create the Container _container = _compositor.CreateContainerVisual(); _container.Size = _ringSize; _container.Offset = new Vector3(((float)finalSize.Width - sideLength) / 2f, ((float)finalSize.Height - sideLength) / 2f, 0f); _container.CenterPoint = new Vector3(_ringSize.X / 2f, _ringSize.Y / 2f, 0f); // Create the Nodes var offset = new Vector3(_nodeRadius, _ringSize.Y / 2f, 0); var centerPoint = new Vector3(_ringSize.X / 2f - _nodeRadius, 0, 0); _nodes = new List <SpriteVisual>(); var geometry = CanvasGeometry.CreateCircle(_generator.Device, new Vector2(_nodeRadius, _nodeRadius), _nodeRadius); // Create/Update the nodeMask var color = NodeColor; if (_nodeMask == null) { //Task.Run(async () => // { _nodeMask = _generator.CreateMask(_nodeSize.ToSize(), geometry, color); // }) //.Wait(); } else { //Task.Run(async () => // { _nodeMask.Redraw(_nodeSize.ToSize(), geometry, color); // }) //.Wait(); } // Create the SurfaceBrush for the nodes var brush = _compositor.CreateSurfaceBrush(_nodeMask.Surface); var baseAngle = 0f; // Create the visuals for the nodes for (var i = 0; i < _maxFramesPerBlock; i++) { var node = _compositor.CreateSpriteVisual(); node.Size = _nodeSize; node.AnchorPoint = new Vector2(0.5f); node.Offset = offset; node.CenterPoint = centerPoint; node.Brush = brush; node.RotationAngleInDegrees = baseAngle; if (i == 0) { baseAngle += _phi; } else if (i == _maxFramesPerBlock - 2) { baseAngle = -_beta; } else { baseAngle += _gamma; } _nodes.Add(node); // Add the visual to the container _container.Children.InsertAtTop(node); } // Add the container to the Visual Tree ElementCompositionPreview.SetElementChildVisual(this, _container); // Start Node animations for (var i = 0; i < _maxFramesPerBlock; i++) { _nodes[i].StartAnimation(AnimatedProperty, _animations[i]); } // Start container animation _containerAnimation = _compositor.CreateScalarKeyFrameAnimation(); _containerAnimation.InsertExpressionKeyFrame(1f, "this.StartingValue + 360f", _compositor.CreateLinearEasingFunction()); _containerAnimation.Duration = RingDuration; _containerAnimation.IterationBehavior = AnimationIterationBehavior.Forever; _container.StartAnimation(AnimatedProperty, _containerAnimation); _isAnimationStarted = true; return(finalSize); }