windows phone 8.1 电子书阅读器中的翻页动画
Posted
技术标签:
【中文标题】windows phone 8.1 电子书阅读器中的翻页动画【英文标题】:Page turn animation in windows phone 8.1 ebook reader 【发布时间】:2017-12-25 18:27:32 【问题描述】:我正在尝试使用 Xamarin Native C# 为 windows phone 8.1 创建一个电子书阅读器。
我已经成功阅读了 eBook 对象,现在我希望根据章节在多个 Web 视图中呈现该数据。网络视图将根据章节长度垂直滚动。但是,我还希望 web 视图在左右滑动时更改章节,最重要的是带有翻页动画。
类似于this sample project,但在 Silverlight 中没有。
附:我开始windows开发才一周,所以请多多包涵。我在这里尽我所能。非常感谢:)
【问题讨论】:
【参考方案1】: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);
【讨论】:
正是我需要的。谢谢【参考方案2】:您可以尝试 CarouselView 以 xamarin 形式实现触发器功能,这里是示例 -https://blog.xamarin.com/flip-through-items-with-xamarin-forms-carouselview/
如果您尝试在 Xamarin Native 中实现,请查看以下 ios 和 android 链接。
IOS-https://github.com/jzeferino/Xamarin.iOS.iCarousel
Android-https://github.com/MobMaxime/xamarin-android-samples/tree/master/CarouselViewProject-master
【讨论】:
首先,我使用的是 Xamarin Native。其次,对于windows phone。 :) 我不知道您的要求是什么,为什么您只使用 xamarin 进行 Windows 应用程序开发。而 Visual Studio 也有 UWP 项目选项来开发 Windows 通用应用程序。以上是关于windows phone 8.1 电子书阅读器中的翻页动画的主要内容,如果未能解决你的问题,请参考以下文章
Windows Phone 8.1 WRT和SQLite,创建数据库时出错
如何使我自己编写的音频播放器可以在后台运行(Windows Phone 8.1- WinRT)?
如何将 Windows 商店中的 Windows Phone 应用程序(8.1 XAML)迁移到 8.1 Silverlight?
csharp 在Windows 8.1和Windows Phone 8.1应用程序中运行的Expander控件的代码隐藏。