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);
        }
예제 #2
0
        /// <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);
        }