windows phone 8.1 电子书中的翻页动画 reader

Page turn animation in windows phone 8.1 ebook reader

我正在尝试在 Xamarin Native C# 中为 windows phone 8.1 创建电子书 reader。

我已经成功阅读了电子书对象,现在我希望根据章节在多个 Web 视图中呈现该数据。网络视图将根据章节长度垂直滚动。但是,我还希望 web 视图通过左右滑动来更改章节,最重要的是带有翻页动画。

类似于 this sample project,但不在 Silverlight 中。

P.S。我开始 windows 开发才一周时间,所以请多多包涵。我在这里尽力而为。非常感谢:)

您可以尝试使用 CarouselView 以 xamarin 形式实现触发器功能,这里是示例 -https://blog.xamarin.com/flip-through-items-with-xamarin-forms-carouselview/

如果您尝试在 Xamarin Native 中实施,请检查下面的 link 以获得 IOS 和 Android。

IOS-https://github.com/jzeferino/Xamarin.iOS.iCarousel

Android-https://github.com/MobMaxime/xamarin-android-samples/tree/master/CarouselViewProject-master

Pageturn.cs(PageTurner7 Silverlight 的实现

using System;
//using System.Windows.Ink;
using System.Collections.Generic;
using Windows.UI.Xaml.Documents;
using System.Windows;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Markup;
using Windows.Foundation;

namespace epub_reader.WinPhone
{
    public class PageTurn
    {
        public PageTurn()
        {
        }

        private int _index = 0;            // Current spread index
        private double _width;             // Width of each page
        private double _height;            // Height of each page
        private int _count = 0;            // Number of even/odd page pairs
        private bool _turning = false;     // True if page turn in progress
        private bool _animating = false;   // True if animated turn completion in progress
        private double _percent = 0.0;     // Percent turned (0 = 0%, 1 = 100%)
        private double _startPos = -1;     // X coordinate of initial mouse click
        private int _direction = 0;        // -1 = Turning left, 1 = Turning right
        private double _step = 0.025;      // Step size for animations
        private double _shadowWidth = 16;  // Maximum shadow width
        private double _shadowBreak = 5;   // Number of degrees required for shadow to attain maximum width
        private double _sensitivity = 1.0; // Higher number -> More mouse movement required for full turn

        private FrameworkElement _owner = null;                      // Owner of mouse capture
        private List<Canvas> _evens = new List<Canvas>();            // Even pages
        private List<Canvas> _odds = new List<Canvas>();             // Odd pages
        private Polygon _shadow = null;                              // Polygon object used to draw shadow
        private Storyboard _timer = null;                            // Storyboard timer for animations
        private Canvas _canvas = null;                               // Canvas containing pages
        private Canvas _workingOdd = null;                           // Working right page
        private Canvas _workingEven = null;                          // Working left page

        private PathGeometry _oddClipRegion = null;
        private LineSegment _oddClipRegionLineSegment1 = null;
        private LineSegment _oddClipRegionLineSegment2 = null;
        private PathGeometry _evenClipRegion = null;
        private LineSegment _evenClipRegionLineSegment1 = null;
        private LineSegment _evenClipRegionLineSegment2 = null;
        private LineSegment _evenClipRegionLineSegment3 = null;
        private TransformGroup _transformGroup = null;
        private RotateTransform _rotateTransform = null;
        private TranslateTransform _translateTransform = null;

        // XAML definition for clip region used on right-hand pages
        private const string _opg =
            "<PathGeometry xmlns=\"http://schemas.microsoft.com/client/2007\">" +
            "<PathFigure StartPoint=\"0,0\">" +
            "<LineSegment />" +
            "<LineSegment />" +
            "</PathFigure>" +
            "</PathGeometry>";

        // XAML definition for clip region used on left-hand pages
        private const string _epg =
            "<PathGeometry xmlns=\"http://schemas.microsoft.com/client/2007\">" +
            "<PathFigure StartPoint=\"0,0\">" +
            "<LineSegment Point=\"0,0\" />" +
            "<LineSegment Point=\"0,0\" />" +
            "<LineSegment Point=\"0,0\" />" +
            "</PathFigure>" +
            "</PathGeometry>";

        // XAML definition for transforms used on left-hand pages
        private const string _tg =
            "<TransformGroup xmlns=\"http://schemas.microsoft.com/client/2007\">" +
            "<RotateTransform />" +
            "<TranslateTransform />" +
            "</TransformGroup>";

        // XAML definition for Storyboard timer
        private const string _sb = "<Storyboard xmlns=\"http://schemas.microsoft.com/client/2007\" Duration=\"0:0:0.01\" />";

        // XAML definition for shadow polygon
        private const string _poly =
            "<Polygon xmlns=\"http://schemas.microsoft.com/client/2007\" Canvas.ZIndex=\"4\" Fill=\"Black\" Opacity=\"0.2\" Points=\"0,0 0,0 0,0 0,0\" Visibility=\"Collapsed\">" +
            "<Polygon.Clip>" +
            "<RectangleGeometry Rect=\"0,0,{0},{1}\" />" +
            "</Polygon.Clip>" +
            "</Polygon>";

        ////////////////////////////////////////////////////////////////////////
        // Events
        public event EventHandler PageTurned;

        ////////////////////////////////////////////////////////////////////////
        // Properties

        public int CurrentSpreadIndex
        {
            get { return _index; }
        }

        public int SpreadCount
        {
            get { return _count; }
        }

        public double Sensitivity
        {
            get { return _sensitivity; }
            set { _sensitivity = value; }
        }

        ////////////////////////////////////////////////////////////////////////
        // Public methods

        public void Initialize(Canvas canvas)
        {
            _canvas = canvas;

            // Create a Storyboard for timing animations
            _timer = (Storyboard)XamlReader.Load(_sb);
            //_timer.Completed += new EventHandler(OnTimerTick);
            _timer.Completed += OnTimerTick;
            // Create a PathGeometry for clipping right-hand pages
            _oddClipRegion = (PathGeometry)XamlReader.Load(_opg);
            _oddClipRegionLineSegment1 = (LineSegment)_oddClipRegion.Figures[0].Segments[0];
            _oddClipRegionLineSegment2 = (LineSegment)_oddClipRegion.Figures[0].Segments[1];

            // Create a PathGeometry for clipping left-hand pages
            string xaml = String.Format(_epg, _evens[0].Height);
            _evenClipRegion = (PathGeometry)XamlReader.Load(xaml);
            _evenClipRegionLineSegment1 = (LineSegment)_evenClipRegion.Figures[0].Segments[0];
            _evenClipRegionLineSegment2 = (LineSegment)_evenClipRegion.Figures[0].Segments[1];
            _evenClipRegionLineSegment3 = (LineSegment)_evenClipRegion.Figures[0].Segments[2];

            // Create a TransformGroup for transforming left-hand pages
            _transformGroup = (TransformGroup)XamlReader.Load(_tg);
            _rotateTransform = (RotateTransform)_transformGroup.Children[0];
            _translateTransform = (TranslateTransform)_transformGroup.Children[1];

            // Initialize internal variables
            _count = _evens.Count;
            _width = _evens[0].Width;
            _height = _evens[0].Height;

            // Create a Polygon to provide shadow during page turns
            _shadow = (Polygon)XamlReader.Load(String.Format(_poly, _width * 2, _height));
            _canvas.Children.Add(_shadow);

            // Initialize z-order
            InitializeZOrder();
        }

        private void OnTimerTick(object sender, object e)
        {
            _percent += _step;

            if (_percent < 0.0)
                _percent = 0.0;
            else if (_percent > 1.0)
                _percent = 1.0;

            TurnTo(_percent);

            if (_percent == 0.0)
            {
                if (_direction == 1)
                {
                    _index--;
                    if (PageTurned != null)
                        PageTurned(this, EventArgs.Empty);
                }
                Reset();
            }
            else if (_percent == 1.0)
            {
                if (_direction == -1)
                {
                    _index++;
                    if (PageTurned != null)
                        PageTurned(this, EventArgs.Empty);
                }
                Reset();
            }
            else
                _timer.Begin();
        }

        public void AddSpread(Canvas left, Canvas right)
        {
            left.PointerPressed += Left_PointerPressed; ; ;
            //left.MouseLeftButtonDown += new MouseButtonEventHandler(OnBeginRightTurn);
            left.PointerMoved += Left_PointerMoved;
            //left.MouseMove += new MouseEventHandler(OnContinueRightTurn);
            left.PointerReleased += Left_PointerReleased; ;
            //left.MouseLeftButtonUp += new MouseButtonEventHandler(OnEndRightTurn);
            _evens.Add(left);

            right.PointerPressed += Right_PointerPressed;
            right.PointerMoved += Right_PointerMoved;
            right.PointerReleased += Right_PointerReleased;
            _odds.Add(right);
        }

        private void Right_PointerPressed(object sender, PointerRoutedEventArgs e)
        {
            if (_animating)
                return; // Do nothing if animation in progress

            // Do nothing if trying to turn left but last
            // page is displayed
            if (_index == _count - 1)
                return;

            // Start a left turn
            _turning = true;
            _direction = -1;
            _percent = 0.0;
            _startPos = e.GetCurrentPoint((FrameworkElement)sender).Position.X;
            _owner = (FrameworkElement)sender;
            ((FrameworkElement)sender).CapturePointer(e.Pointer);

            // Turn page to specified angle
            TurnTo(_percent);

            // Cache references to "working" pages
            _workingOdd = _odds[_index];
            _workingEven = _evens[_index + 1];

            // Assign clipping regions and transforms to relevant canvases
            RectangleGeometry rect = new RectangleGeometry();
            rect.Transform = _oddClipRegion.Transform;
            rect.Rect = _oddClipRegion.Bounds;
            _workingOdd.Clip = rect;

            //_workingOdd.Clip = _oddClipRegion;
            //_workingEven.Clip = _evenClipRegion;
            RectangleGeometry rect2 = new RectangleGeometry();
            rect2.Rect = _evenClipRegion.Bounds;
            rect2.Transform = _evenClipRegion.Transform;
            _workingEven.Clip = rect2;

            _workingEven.RenderTransform = _transformGroup;

            // Set z-indexes for a left turn
            _evens[_index + 1].SetValue(Canvas.ZIndexProperty, 2);
            _odds[_index + 1].SetValue(Canvas.ZIndexProperty, 0);
        }

        private void Right_PointerMoved(object sender, PointerRoutedEventArgs e)
        {
            if (_animating)
                return; // Do nothing if animation in progress

            if (_turning)
            {
                // Compute change in X
                double dx = _startPos - e.GetCurrentPoint((FrameworkElement)sender).Position.X;

                // If mouse moved right, update _startPos so page
                // begins turning with first move left
                if (dx < 0)
                {
                    _startPos = e.GetCurrentPoint((FrameworkElement)sender).Position.X;
                    return;
                }

                // Compute turn percentage based on change in X
                double percent = dx / (_width * _sensitivity);

                if (percent > 1.0)
                    percent = 1.0;
                else if (percent < 0.0)
                    percent = 0.0;

                // Exit now if no change
                if (percent == _percent)
                    return;

                // Update percent turned
                _percent = percent;

                // Turn page to specified angle
                TurnTo(_percent);
            }
        }

        private void Right_PointerReleased(object sender, PointerRoutedEventArgs e)
        {
            if (_animating)
                return; // Do nothing if animation in progress

            if (_turning)
                CompleteTurn();
        }

        private void Left_PointerPressed(object sender, PointerRoutedEventArgs e)
        {
            if (_animating)
                return; // Do nothing if animation in progress

            // Do nothing if trying to turn right but first
            // page is displayed
            if (_index == 0)
                return;

            // Start a right turn
            _turning = true;
            _direction = 1;
            _percent = 1.0;
            _startPos = e.GetCurrentPoint((FrameworkElement)sender).Position.X;
            _owner = (FrameworkElement)sender;
            ((FrameworkElement)sender).CapturePointer(e.Pointer);

            // Turn page to specified angle
            TurnTo(_percent);

            // Cache references to "working" pages
            _workingOdd = _odds[_index - 1];
            _workingEven = _evens[_index];

            // Assign clipping regions and transforms to relevant canvases
            RectangleGeometry rect = new RectangleGeometry();
            rect.Transform = _oddClipRegion.Transform;
            rect.Rect = _oddClipRegion.Bounds;
            _workingOdd.Clip = rect;

            //_workingOdd.Clip = _oddClipRegion;
            //_workingEven.Clip = _evenClipRegion;
            RectangleGeometry rect2 = new RectangleGeometry();
            rect2.Rect = _evenClipRegion.Bounds;
            rect2.Transform = _evenClipRegion.Transform;
            _workingEven.Clip = rect2;
            _workingEven.RenderTransform = _transformGroup;

            // Set z-indexes for a right turn
            _evens[_index].SetValue(Canvas.ZIndexProperty, 3);
            _evens[_index - 1].SetValue(Canvas.ZIndexProperty, 0);
            _odds[_index - 1].SetValue(Canvas.ZIndexProperty, 2);

        }

        private void Left_PointerMoved(object sender, PointerRoutedEventArgs e)
        {
            if (_animating)
                return; // Do nothing if animation in progress

            if (_turning)
            {
                // Compute change in X
                double dx = e.GetCurrentPoint((FrameworkElement)(((FrameworkElement)sender).Parent)).Position.X - _startPos;

                // If mouse moved left, update _startPos so page
                // begins turning with first move right
                if (dx < 0)
                {
                    _startPos = e.GetCurrentPoint((FrameworkElement)(((FrameworkElement)sender).Parent)).Position.X;
                    return;
                }

                // Compute turn percentage based on change in X
                double percent = 1.0 - (dx / (_width * _sensitivity));

                if (percent > 1.0)
                    percent = 1.0;
                else if (percent < 0.0)
                    percent = 0.0;

                // Exit now if no change
                if (percent == _percent)
                    return;

                // Update percent turned
                _percent = percent;

                // Turn page to specified angle
                TurnTo(this._percent);
            }
        }

        private void Left_PointerReleased(object sender, PointerRoutedEventArgs e)
        {
            if (_animating)
                return; // Do nothing if animation in progress

            if (_turning)
                CompleteTurn();
        }

        public void GoToSpread(int index)
        {
            if (index != _index && index > 0 && index < _count)
            {
                _index = index;
                InitializeZOrder();

                if (PageTurned != null)
                    PageTurned(this, EventArgs.Empty);
            }
        }

        ////////////////////////////////////////////////////////////////////////
        // Helper methods

        private void TurnTo(double percent)
        {
            // Compute angle of rotation
            double degrees = 45 - (percent * 45);
            double radians = degrees * Math.PI / 180;

            // Compute x coordinates along bottom of canvas
            double dx1 = _width - (percent * _width);
            double dx2 = _width - dx1;

            // Compute tangent of rotation angle
            double tan = Math.Tan(radians);

            // Configure clipping region for right-hand page
            double p2y;

            if (tan == 0)
                p2y = _height;
            else
                p2y = _height + (dx1 / tan);

            double p3x = p2y * tan;

            _oddClipRegionLineSegment1.Point = new Point(0, p2y);
            _oddClipRegionLineSegment2.Point = new Point(p3x, 0);

            // Configure clipping region for left-hand page
            double p7x = dx2 - (_height * tan);

            if (p7x >= 0.0) // 4-corner clipping region
            {
                _evenClipRegion.Figures[0].StartPoint = new Point(0, 0);
                _evenClipRegionLineSegment1.Point = new Point(0, _height);
                _evenClipRegionLineSegment2.Point = new Point(dx2, _height);
                _evenClipRegionLineSegment3.Point = new Point(p7x, 0);
            }
            else // 3-corner clipping region
            {
                double y = _height - (dx2 / tan);
                _evenClipRegion.Figures[0].StartPoint = _evenClipRegionLineSegment3.Point = new Point(0, y);
                _evenClipRegionLineSegment1.Point = new Point(0, _height);
                _evenClipRegionLineSegment2.Point = new Point(dx2, _height);
            }

            // Apply clipping regions and transforms
            _rotateTransform.CenterX = dx2;
            _rotateTransform.CenterY = _height;
            _rotateTransform.Angle = 2 * degrees;

            _translateTransform.X = 2 * (_width - dx2);

            // Configure shadow
            if (percent == 0.0 || percent == 1.0)
            {
                _shadow.Visibility = Visibility.Collapsed;
                return;
            }

            _shadow.Visibility = Visibility.Visible;

            double min = this._shadowBreak;
            double max = 45 - this._shadowBreak;
            double width;

            if (degrees > min && degrees < max)
                width = _shadowWidth;
            else
            {
                if (degrees <= min)
                    width = (degrees / _shadowBreak) * _shadowWidth;
                else // degrees >= max
                    width = ((45 - degrees) / _shadowBreak) * _shadowWidth;
            }

            double x1 = _width + dx1 + (_height * tan);
            double x2 = _width + dx1;
            double y2 = _height;
            double x3 = x2 + width;
            double y3 = _height;
            double x4 = x1 + width;

            _shadow.Points[0] = new Point(x1, 0);
            _shadow.Points[1] = new Point(x2, y2);
            _shadow.Points[2] = new Point(x3, y3);
            _shadow.Points[3] = new Point(x4, 0);
        }

        private void CompleteTurn()
        {
            if (_percent == 0.0)
            {
                if (_direction == 1)
                {
                    _index--;
                    if (PageTurned != null)
                        PageTurned(this, EventArgs.Empty);
                }
                Reset();
                return;
            }

            if (_percent == 1.0)
            {
                if (_direction == -1)
                {
                    _index++;
                    if (PageTurned != null)
                        PageTurned(this, EventArgs.Empty);
                }
                Reset();
                return;
            }

            if (_percent < 0.5)
                _step = -Math.Abs(_step);
            else
                _step = Math.Abs(_step);

            _animating = true;
            _timer.Begin();
        }

        private void Reset()
        {
            _turning = false;
            _animating = false;
            _direction = 0;

            if (_owner != null)
                _owner.ReleasePointerCaptures();
            _owner = null;

            if (_workingOdd != null && _workingOdd.Clip != null)
                _workingOdd.Clip = null;
            if (_workingEven != null && _workingEven.Clip != null)
                _workingEven.Clip = null;
            if (_workingEven != null && _workingEven.RenderTransform != null)
                _workingEven.RenderTransform = null;

            _workingOdd = null;
            _workingEven = null;

            _shadow.Visibility = Visibility.Collapsed;

            InitializeZOrder();
        }

        private void InitializeZOrder()
        {
            for (int i = 0; i < _count; i++)
            {
                _evens[i].SetValue(Canvas.ZIndexProperty, (i == _index) ? 1 : -1);
                _odds[i].SetValue(Canvas.ZIndexProperty, (i == _index) ? 1 : -1);
            }
        }
    }
}