feature: slide animation frames
This commit is contained in:
parent
08ad76d8de
commit
0d5a1108ab
@ -1,9 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<u:AdaptedPage xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:mdl="clr-namespace:Pixiview.Illust"
|
||||
xmlns:u="clr-namespace:Pixiview.UI"
|
||||
xmlns:util="clr-namespace:Pixiview.Utils"
|
||||
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
|
||||
x:Class="Pixiview.Illust.ViewIllustPage"
|
||||
ios:Page.UseSafeArea="False"
|
||||
@ -13,43 +11,22 @@
|
||||
<ContentPage.ToolbarItems>
|
||||
<ToolbarItem Order="Primary" Clicked="Favorite_Clicked"
|
||||
IconImageSource="{Binding FavoriteIcon}"/>
|
||||
<ToolbarItem Order="Primary" Clicked="More_Clicked"
|
||||
IconImageSource="{DynamicResource FontIconMore}"/>
|
||||
</ContentPage.ToolbarItems>
|
||||
<Grid Padding="{Binding PageTopMargin}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<CarouselView ItemsSource="{Binding Illusts}" HorizontalScrollBarVisibility="Never"
|
||||
Position="{Binding CurrentPage}"
|
||||
ItemTemplate="{StaticResource carouselView}"
|
||||
IsScrollAnimated="{Binding IsScrollAnimated}"
|
||||
PositionChanged="CarouselView_PositionChanged">
|
||||
<CarouselView.ItemsLayout>
|
||||
<LinearItemsLayout Orientation="Vertical" ItemSpacing="20"/>
|
||||
</CarouselView.ItemsLayout>
|
||||
<!--<CarouselView.ItemTemplate>
|
||||
<DataTemplate x:DataType="mdl:IllustDetailItem">
|
||||
<Grid>
|
||||
<Image Source="{Binding Image}"
|
||||
HorizontalOptions="Fill" VerticalOptions="Fill"
|
||||
Aspect="AspectFit"
|
||||
util:LongPressEffect.Command="{Binding LongPressed}"
|
||||
util:LongPressEffect.CommandParameter="{Binding .}">
|
||||
<Image.Effects>
|
||||
<util:LongPressEffect/>
|
||||
</Image.Effects>
|
||||
<Image.GestureRecognizers>
|
||||
<TapGestureRecognizer Tapped="Image_Tapped"/>
|
||||
</Image.GestureRecognizers>
|
||||
</Image>
|
||||
<Frame HasShadow="False" Margin="0" Padding="20" CornerRadius="8"
|
||||
IsVisible="{Binding Loading}"
|
||||
HorizontalOptions="Center" VerticalOptions="Center"
|
||||
BackgroundColor="{DynamicResource MaskColor}">
|
||||
<ActivityIndicator IsRunning="True" IsVisible="True"
|
||||
Color="{DynamicResource WindowColor}"/>
|
||||
</Frame>
|
||||
<ActivityIndicator IsRunning="True" IsVisible="{Binding Downloading}"
|
||||
Margin="10"
|
||||
HorizontalOptions="Start" VerticalOptions="Start"
|
||||
Color="{DynamicResource TextColor}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</CarouselView.ItemTemplate>-->
|
||||
</CarouselView>
|
||||
|
||||
<u:RoundLabel Text="{Binding PagePositionText}"
|
||||
@ -58,5 +35,13 @@
|
||||
HorizontalOptions="End" VerticalOptions="Start"
|
||||
FontSize="Micro" TextColor="White"
|
||||
IsVisible="{Binding IsPageVisible}"/>
|
||||
|
||||
<Slider Grid.Row="1" VerticalOptions="End"
|
||||
Margin="{DynamicResource ScreenBottomPadding}"
|
||||
MinimumTrackColor="{DynamicResource TintColor}"
|
||||
IsEnabled="{Binding IsAnimateSliderEnabled}"
|
||||
IsVisible="{Binding IsAnimateSliderVisible}"
|
||||
Value="{Binding CurrentAnimeFrame, Mode=TwoWay}"
|
||||
Maximum="{Binding MaximumFrame}"/>
|
||||
</Grid>
|
||||
</u:AdaptedPage>
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Pixiview.Resources;
|
||||
using Pixiview.UI;
|
||||
using Pixiview.UI.Theme;
|
||||
@ -22,10 +21,51 @@ namespace Pixiview.Illust
|
||||
nameof(Illusts), typeof(IllustDetailItem[]), typeof(ViewIllustPage));
|
||||
public static readonly BindableProperty PagePositionTextProperty = BindableProperty.Create(
|
||||
nameof(PagePositionText), typeof(string), typeof(ViewIllustPage));
|
||||
public static readonly BindableProperty IsPageVisibleProperty = BindableProperty.Create(
|
||||
nameof(IsPageVisible), typeof(bool), typeof(ViewIllustPage));
|
||||
public static readonly BindableProperty IllustItemProperty = BindableProperty.Create(
|
||||
nameof(IllustItem), typeof(IllustItem), typeof(ViewIllustPage));
|
||||
public static readonly BindableProperty CurrentPageProperty = BindableProperty.Create(
|
||||
nameof(CurrentPage), typeof(int), typeof(ViewIllustPage), propertyChanged: OnCurrentPagePropertyChanged);
|
||||
public static readonly BindableProperty IsScrollAnimatedProperty = BindableProperty.Create(
|
||||
nameof(IsScrollAnimated), typeof(bool), typeof(ViewIllustPage), true);
|
||||
public static readonly BindableProperty IsAnimateSliderVisibleProperty = BindableProperty.Create(
|
||||
nameof(IsAnimateSliderVisible), typeof(bool), typeof(ViewIllustPage));
|
||||
public static readonly BindableProperty IsAnimateSliderEnabledProperty = BindableProperty.Create(
|
||||
nameof(IsAnimateSliderEnabled), typeof(bool), typeof(ViewIllustPage));
|
||||
public static readonly BindableProperty CurrentAnimeFrameProperty = BindableProperty.Create(
|
||||
nameof(CurrentAnimeFrame), typeof(double), typeof(ViewIllustPage), propertyChanged: OnCurrentAnimeFramePropertyChanged);
|
||||
public static readonly BindableProperty MaximumFrameProperty = BindableProperty.Create(
|
||||
nameof(MaximumFrame), typeof(double), typeof(ViewIllustPage), 1.0);
|
||||
|
||||
private static void OnCurrentPagePropertyChanged(BindableObject obj, object old, object @new)
|
||||
{
|
||||
var page = (ViewIllustPage)obj;
|
||||
var index = (int)@new;
|
||||
var items = page.Illusts;
|
||||
var length = items.Length;
|
||||
page.PagePositionText = $"{index + 1}/{length}";
|
||||
|
||||
var item = items[index];
|
||||
if (!item.Loading && item.Image == null)
|
||||
{
|
||||
Task.Run(() => page.DoLoadImage(index));
|
||||
}
|
||||
if (index < length - 1)
|
||||
{
|
||||
item = items[index + 1];
|
||||
if (!item.Loading && item.Image == null)
|
||||
{
|
||||
Task.Run(() => page.DoLoadImage(index + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnCurrentAnimeFramePropertyChanged(BindableObject obj, object old, object @new)
|
||||
{
|
||||
var page = (ViewIllustPage)obj;
|
||||
if (page.ugoira != null && page.IsAnimateSliderEnabled)
|
||||
{
|
||||
var frame = (double)@new;
|
||||
page.ugoira.ToggleFrame((int)frame);
|
||||
}
|
||||
}
|
||||
|
||||
public ImageSource FavoriteIcon
|
||||
{
|
||||
@ -43,21 +83,41 @@ namespace Pixiview.Illust
|
||||
get => (string)GetValue(PagePositionTextProperty);
|
||||
set => SetValue(PagePositionTextProperty, value);
|
||||
}
|
||||
public bool IsPageVisible
|
||||
public int CurrentPage
|
||||
{
|
||||
get => (bool)GetValue(IsPageVisibleProperty);
|
||||
set => SetValue(IsPageVisibleProperty, value);
|
||||
get => (int)GetValue(CurrentPageProperty);
|
||||
private set => SetValue(CurrentPageProperty, value);
|
||||
}
|
||||
public IllustItem IllustItem
|
||||
public bool IsScrollAnimated
|
||||
{
|
||||
get => (IllustItem)GetValue(IllustItemProperty);
|
||||
set => SetValue(IllustItemProperty, value);
|
||||
get => (bool)GetValue(IsScrollAnimatedProperty);
|
||||
set => SetValue(IsScrollAnimatedProperty, value);
|
||||
}
|
||||
public bool IsAnimateSliderVisible
|
||||
{
|
||||
get => (bool)GetValue(IsAnimateSliderVisibleProperty);
|
||||
set => SetValue(IsAnimateSliderVisibleProperty, value);
|
||||
}
|
||||
public bool IsAnimateSliderEnabled
|
||||
{
|
||||
get => (bool)GetValue(IsAnimateSliderEnabledProperty);
|
||||
set => SetValue(IsAnimateSliderEnabledProperty, value);
|
||||
}
|
||||
public double CurrentAnimeFrame
|
||||
{
|
||||
get => (double)GetValue(CurrentAnimeFrameProperty);
|
||||
set => SetValue(CurrentAnimeFrameProperty, value);
|
||||
}
|
||||
public double MaximumFrame
|
||||
{
|
||||
get => (double)GetValue(MaximumFrameProperty);
|
||||
set => SetValue(MaximumFrameProperty, value);
|
||||
}
|
||||
|
||||
public int CurrentPage { get; private set; }
|
||||
public IllustItem IllustItem { get; private set; }
|
||||
public bool IsPageVisible { get; private set; }
|
||||
|
||||
private readonly bool saveFavorites;
|
||||
private readonly ICommand longPressed;
|
||||
private readonly ImageSource fontIconLove;
|
||||
private readonly ImageSource fontIconNotLove;
|
||||
private IllustUgoiraData ugoiraData;
|
||||
@ -67,7 +127,6 @@ namespace Pixiview.Illust
|
||||
{
|
||||
IllustItem = illust;
|
||||
saveFavorites = save;
|
||||
longPressed = new Command<IllustDetailItem>(Illust_LongPressed);
|
||||
BindingContext = this;
|
||||
|
||||
fontIconLove = (ImageSource)Application.Current.Resources[ThemeBase.FontIconLove];
|
||||
@ -77,7 +136,9 @@ namespace Pixiview.Illust
|
||||
? fontIconLove
|
||||
: fontIconNotLove;
|
||||
|
||||
Resources.Add("carouselView", GetCarouseTemplate());
|
||||
var pageVisible = illust != null && illust.PageCount > 1;
|
||||
IsPageVisible = pageVisible;
|
||||
Resources.Add("carouselView", GetCarouseTemplate(pageVisible));
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
@ -115,65 +176,111 @@ namespace Pixiview.Illust
|
||||
Screen.SetHomeIndicatorAutoHidden(Shell.Current, false);
|
||||
}
|
||||
|
||||
private DataTemplate GetCarouseTemplate()
|
||||
private DataTemplate GetCarouseTemplate(bool multiPages)
|
||||
{
|
||||
var isAnime = IllustItem.IllustType == IllustType.Anime;
|
||||
var tap = new TapGestureRecognizer();
|
||||
tap.Tapped += Image_Tapped;
|
||||
|
||||
return new DataTemplate(() =>
|
||||
{
|
||||
// image
|
||||
var image = new Image
|
||||
{
|
||||
HorizontalOptions = LayoutOptions.Fill,
|
||||
VerticalOptions = LayoutOptions.Fill,
|
||||
Aspect = Aspect.AspectFit
|
||||
Aspect = Aspect.AspectFit,
|
||||
GestureRecognizers = { tap }
|
||||
}
|
||||
.Binding(Image.SourceProperty, nameof(IllustDetailItem.Image));
|
||||
if (isAnime)
|
||||
|
||||
// downloading
|
||||
var downloading = new Frame
|
||||
{
|
||||
image.GestureRecognizers.Add(tap);
|
||||
HasShadow = false,
|
||||
Margin = default,
|
||||
Padding = new Thickness(20),
|
||||
CornerRadius = 8,
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
VerticalOptions = LayoutOptions.Center,
|
||||
Content = new ActivityIndicator
|
||||
{
|
||||
IsRunning = true,
|
||||
IsVisible = true
|
||||
}
|
||||
.DynamicResource(ActivityIndicator.ColorProperty, ThemeBase.WindowColor)
|
||||
}
|
||||
else
|
||||
.Binding(IsVisibleProperty, nameof(IllustDetailItem.Loading))
|
||||
.DynamicResource(BackgroundColorProperty, ThemeBase.MaskColor);
|
||||
|
||||
// loading original
|
||||
var original = new ActivityIndicator
|
||||
{
|
||||
image.SetBinding(LongPressEffect.CommandProperty, nameof(IllustDetailItem.LongPressed));
|
||||
image.SetBinding(LongPressEffect.CommandParameterProperty, ".");
|
||||
image.Effects.Add(new LongPressEffect());
|
||||
IsRunning = true,
|
||||
Margin = new Thickness(10),
|
||||
HorizontalOptions = LayoutOptions.Start,
|
||||
VerticalOptions = LayoutOptions.Start
|
||||
}
|
||||
.Binding(IsVisibleProperty, nameof(IllustDetailItem.Downloading))
|
||||
.DynamicResource(ActivityIndicator.ColorProperty, ThemeBase.TextColor);
|
||||
|
||||
if (multiPages)
|
||||
{
|
||||
var tapPrevious = new TapGestureRecognizer();
|
||||
tapPrevious.Tapped += TapPrevious_Tapped;
|
||||
var tapNext = new TapGestureRecognizer();
|
||||
tapNext.Tapped += TapNext_Tapped;
|
||||
|
||||
return new Grid
|
||||
{
|
||||
Children =
|
||||
{
|
||||
// image
|
||||
image,
|
||||
|
||||
// tap holder
|
||||
new Grid
|
||||
{
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition(),
|
||||
new RowDefinition(),
|
||||
new RowDefinition()
|
||||
},
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
GestureRecognizers = { tapPrevious }
|
||||
},
|
||||
new Label
|
||||
{
|
||||
GestureRecognizers = { tapNext }
|
||||
}
|
||||
.GridRow(2)
|
||||
}
|
||||
},
|
||||
|
||||
// downloading
|
||||
downloading,
|
||||
|
||||
// loading original
|
||||
original
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return new Grid
|
||||
{
|
||||
Children =
|
||||
{
|
||||
// image
|
||||
image,
|
||||
|
||||
new Frame
|
||||
{
|
||||
HasShadow = false,
|
||||
Margin = default,
|
||||
Padding = new Thickness(20),
|
||||
CornerRadius = 8,
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
VerticalOptions = LayoutOptions.Center,
|
||||
Content = new ActivityIndicator
|
||||
{
|
||||
IsRunning = true,
|
||||
IsVisible = true
|
||||
}
|
||||
.DynamicResource(ActivityIndicator.ColorProperty, ThemeBase.WindowColor)
|
||||
}
|
||||
.Binding(IsVisibleProperty, nameof(IllustDetailItem.Loading))
|
||||
.DynamicResource(BackgroundColorProperty, ThemeBase.MaskColor),
|
||||
// downloading
|
||||
downloading,
|
||||
|
||||
new ActivityIndicator
|
||||
{
|
||||
IsRunning = true,
|
||||
Margin = new Thickness(10),
|
||||
HorizontalOptions = LayoutOptions.Start,
|
||||
VerticalOptions = LayoutOptions.Start
|
||||
}
|
||||
.Binding(IsVisibleProperty, nameof(IllustDetailItem.Downloading))
|
||||
.DynamicResource(ActivityIndicator.ColorProperty, ThemeBase.TextColor)
|
||||
// loading original
|
||||
original
|
||||
}
|
||||
};
|
||||
});
|
||||
@ -189,20 +296,14 @@ namespace Pixiview.Illust
|
||||
var items = new IllustDetailItem[illust.PageCount];
|
||||
if (items.Length > 1)
|
||||
{
|
||||
IsPageVisible = true;
|
||||
PagePositionText = $"1/{items.Length}";
|
||||
}
|
||||
else
|
||||
{
|
||||
IsPageVisible = false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < items.Length; i++)
|
||||
{
|
||||
items[i] = new IllustDetailItem
|
||||
{
|
||||
Id = illust.Id,
|
||||
LongPressed = longPressed
|
||||
Id = illust.Id
|
||||
};
|
||||
if (i == 0)
|
||||
{
|
||||
@ -234,8 +335,7 @@ namespace Pixiview.Illust
|
||||
{
|
||||
tmp[i] = new IllustDetailItem
|
||||
{
|
||||
Id = illustItem.Id,
|
||||
LongPressed = longPressed
|
||||
Id = illustItem.Id
|
||||
};
|
||||
}
|
||||
Illusts = items = tmp;
|
||||
@ -277,6 +377,12 @@ namespace Pixiview.Illust
|
||||
{
|
||||
// anime
|
||||
ugoiraData = Stores.LoadIllustUgoiraData(illustItem.Id);
|
||||
if (ugoiraData != null && ugoiraData.body != null)
|
||||
{
|
||||
var length = ugoiraData.body.frames.Length;
|
||||
MaximumFrame = length > 0 ? length : 1;
|
||||
IsAnimateSliderVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -309,24 +415,29 @@ namespace Pixiview.Illust
|
||||
|
||||
private void CarouselView_PositionChanged(object sender, PositionChangedEventArgs e)
|
||||
{
|
||||
var index = e.CurrentPosition;
|
||||
CurrentPage = index;
|
||||
var items = Illusts;
|
||||
var length = items.Length;
|
||||
PagePositionText = $"{index + 1}/{length}";
|
||||
CurrentPage = e.CurrentPosition;
|
||||
}
|
||||
|
||||
var item = items[index];
|
||||
if (!item.Loading && item.Image == null)
|
||||
private void TapPrevious_Tapped(object sender, EventArgs e)
|
||||
{
|
||||
var index = CurrentPage;
|
||||
if (index > 0)
|
||||
{
|
||||
Task.Run(() => DoLoadImage(index));
|
||||
IsScrollAnimated = false;
|
||||
CurrentPage = index - 1;
|
||||
IsScrollAnimated = true;
|
||||
}
|
||||
if (index < length - 1)
|
||||
}
|
||||
|
||||
private void TapNext_Tapped(object sender, EventArgs e)
|
||||
{
|
||||
var index = CurrentPage;
|
||||
var illusts = Illusts;
|
||||
if (illusts != null && index < illusts.Length - 1)
|
||||
{
|
||||
item = items[index + 1];
|
||||
if (!item.Loading && item.Image == null)
|
||||
{
|
||||
Task.Run(() => DoLoadImage(index + 1));
|
||||
}
|
||||
IsScrollAnimated = false;
|
||||
CurrentPage = index + 1;
|
||||
IsScrollAnimated = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,6 +471,7 @@ namespace Pixiview.Illust
|
||||
if (ugoira != null)
|
||||
{
|
||||
var playing = !ugoira.IsPlaying;
|
||||
IsAnimateSliderEnabled = !playing;
|
||||
ugoira.TogglePlay(playing);
|
||||
illustItem.IsPlaying = playing;
|
||||
}
|
||||
@ -381,10 +493,20 @@ namespace Pixiview.Illust
|
||||
private void OnUgoiraFrameChanged(object sender, UgoiraEventArgs e)
|
||||
{
|
||||
e.DetailItem.Image = e.Image;
|
||||
CurrentAnimeFrame = e.FrameIndex;
|
||||
}
|
||||
|
||||
private async void Illust_LongPressed(IllustDetailItem item)
|
||||
private async void More_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
int p = CurrentPage;
|
||||
var illusts = Illusts;
|
||||
if (illusts == null || p < 0 || p >= illusts.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var item = illusts[p];
|
||||
|
||||
List<string> extras = new List<string>();
|
||||
var share = ResourceHelper.Share;
|
||||
var preview = Stores.GetPreviewImagePath(item.PreviewUrl);
|
||||
@ -496,7 +618,6 @@ namespace Pixiview.Illust
|
||||
set => SetValue(DownloadingProperty, value);
|
||||
}
|
||||
public string Id { get; set; }
|
||||
public ICommand LongPressed { get; set; }
|
||||
public string PreviewUrl { get; set; }
|
||||
public string OriginalUrl { get; set; }
|
||||
}
|
||||
|
@ -302,6 +302,7 @@ namespace Pixiview.Utils
|
||||
private int index = 0;
|
||||
|
||||
public bool IsPlaying { get; private set; }
|
||||
public readonly int FrameCount;
|
||||
public event EventHandler<UgoiraEventArgs> FrameChanged;
|
||||
|
||||
public Ugoira(IllustUgoiraData illust, IllustDetailItem item)
|
||||
@ -309,6 +310,7 @@ namespace Pixiview.Utils
|
||||
ugoira = illust.body;
|
||||
detailItem = item;
|
||||
frames = new ImageSource[ugoira.frames.Length];
|
||||
FrameCount = frames.Length;
|
||||
timer = new Timer(OnTimerCallback, null, Timeout.Infinite, Timeout.Infinite);
|
||||
|
||||
Task.Run(LoadFrames);
|
||||
@ -332,6 +334,30 @@ namespace Pixiview.Utils
|
||||
}
|
||||
}
|
||||
|
||||
public void ToggleFrame(int frame)
|
||||
{
|
||||
if (IsPlaying)
|
||||
{
|
||||
// TODO: doesn't support change current frame when playing
|
||||
return;
|
||||
}
|
||||
if (frame < 0 || frame >= frames.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var image = frames[frame];
|
||||
if (image != null)
|
||||
{
|
||||
index = frame;
|
||||
FrameChanged?.Invoke(this, new UgoiraEventArgs
|
||||
{
|
||||
DetailItem = detailItem,
|
||||
Image = image,
|
||||
FrameIndex = frame
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTimerCallback(object state)
|
||||
{
|
||||
if (!IsPlaying)
|
||||
@ -350,7 +376,8 @@ namespace Pixiview.Utils
|
||||
FrameChanged?.Invoke(this, new UgoiraEventArgs
|
||||
{
|
||||
DetailItem = detailItem,
|
||||
Image = frame
|
||||
Image = frame,
|
||||
FrameIndex = i
|
||||
});
|
||||
i++;
|
||||
if (i >= frames.Length)
|
||||
@ -498,5 +525,6 @@ namespace Pixiview.Utils
|
||||
{
|
||||
public IllustDetailItem DetailItem { get; set; }
|
||||
public ImageSource Image { get; set; }
|
||||
public int FrameIndex { get; set; }
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user