Проблема анимации раскадровки WPF при использовании VisualBrush
Я играл с раскадровками, анимацией и визуальными кистями. Я столкнулся с проблемой, хотя. Ниже приведен xaml и программный код небольшого примера, который я быстро собрал, чтобы попытаться продемонстрировать проблему.
Когда вы впервые запускаете приложение, вы видите красный квадрат и две кнопки. Если вы нажмете кнопку "Перевернуть", красный квадрат "перевернется" и появится синий. В действительности все, что происходит, это то, что масштаб ширины StackPanel, в котором находится красный квадрат, уменьшается до тех пор, пока он не достигнет нуля, а затем StackPanel, где находится синий квадрат, ширина которого изначально масштабируется до нуля, имеет ширина увеличилась. Если вы нажмете кнопку "Перевернуть" несколько раз, анимация выглядит хорошо и плавно.
Теперь, если вы нажмете кнопку "Отражение", отражение красных / синих кнопок будет добавлено к их соответствующим стекам. Нажатие на кнопку "Перевернуть" теперь будет вызывать анимацию переворота, но это больше не плавная анимация. Ширина StackPanels часто не уменьшается до нуля. Ширина несколько уменьшается, но затем просто останавливается, прежде чем становится полностью невидимой. Затем другая StackPanel появляется как обычно. Единственное, что изменилось, - это добавление отражения, которое является просто VisualBrush.
Ниже приведен код. У кого-нибудь есть идеи, почему анимации отличаются между двумя случаями (во втором случае остановка)?
Благодарю.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xml:lang="en-US"
xmlns:d="http://schemas.microsoft.com/expression/blend/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
x:Class="WpfFlipTest.Window1"
x:Name="Window"
Title="Window1"
Width="214" Height="224">
<Window.Resources>
<Storyboard x:Key="sbFlip">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="redStack" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00.4" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00.4" Storyboard.TargetName="blueStack" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00.8" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="sbFlipBack">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="blueStack" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00.4" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00.4" Storyboard.TargetName="redStack" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00.8" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Grid x:Name="LayoutRoot" Background="Gray">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Name="redStack" Grid.Row="0" Grid.Column="0" RenderTransformOrigin="0.5,0.5">
<StackPanel.RenderTransform>
<ScaleTransform/>
</StackPanel.RenderTransform>
<Border Name="redBorder" BorderBrush="Transparent" BorderThickness="4" Width="Auto" Height="Auto">
<Button Margin="0" Name="redButton" Height="75" Background="Red" Width="105" />
</Border>
<Border Width="{Binding ElementName=redBorder, Path=ActualWidth}"
Height="{Binding ElementName=redBorder, Path=ActualHeight}"
Opacity="0.2" BorderBrush="Transparent" BorderThickness="4" Name="redRefelction" Visibility="Collapsed">
<Border.OpacityMask>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0" Color="Black"/>
<GradientStop Offset=".6" Color="Transparent"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.OpacityMask>
<Border.Background>
<VisualBrush Visual="{Binding ElementName=redButton}">
<VisualBrush.Transform>
<ScaleTransform ScaleX="1" ScaleY="-1"
CenterX="52.5"
CenterY="37.5" />
</VisualBrush.Transform>
</VisualBrush>
</Border.Background>
</Border>
</StackPanel>
<StackPanel Name="blueStack" Grid.Row="0" Grid.Column="0" RenderTransformOrigin="0.5,0.5">
<StackPanel.RenderTransform>
<ScaleTransform ScaleX="0"/>
</StackPanel.RenderTransform>
<Border Name="blueBorder" BorderBrush="Transparent" BorderThickness="4" Width="Auto" Height="Auto">
<Button Grid.Row="0" Grid.Column="1" Margin="0" Width="105" Background="Blue" Name="blueButton" Height="75"/>
</Border>
<Border Width="{Binding ElementName=blueBorder, Path=ActualWidth}"
Height="{Binding ElementName=blueBorder, Path=ActualHeight}"
Opacity="0.2" BorderBrush="Transparent" BorderThickness="4" Name="blueRefelction" Visibility="Collapsed">
<Border.OpacityMask>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0" Color="Black"/>
<GradientStop Offset=".6" Color="Transparent"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.OpacityMask>
<Border.Background>
<VisualBrush Visual="{Binding ElementName=blueButton}">
<VisualBrush.Transform>
<ScaleTransform ScaleX="1" ScaleY="-1"
CenterX="52.5"
CenterY="37.5" />
</VisualBrush.Transform>
</VisualBrush>
</Border.Background>
</Border>
</StackPanel>
<Button Grid.Row="1" Click="FlipButton_Click" Height="19.45" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76">Flip</Button>
<Button Grid.Row="0" Grid.Column="1" Click="ReflectionButton_Click" Height="19.45" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76">Reflection</Button>
</Grid>
</Window>
Вот обработчики нажатия кнопки:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Animation;
namespace WpfFlipTest
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
bool flipped = false;
private void FlipButton_Click(object sender, RoutedEventArgs e)
{
Storyboard sbFlip = (Storyboard)Resources["sbFlip"];
Storyboard sbFlipBack = (Storyboard)Resources["sbFlipBack"];
if (flipped)
{
sbFlipBack.Begin();
flipped = false;
}
else
{
sbFlip.Begin();
flipped = true;
}
}
bool reflection = false;
private void ReflectionButton_Click(object sender, RoutedEventArgs e)
{
if (reflection)
{
reflection = false;
redRefelction.Visibility = Visibility.Collapsed;
blueRefelction.Visibility = Visibility.Collapsed;
}
else
{
reflection = true;
redRefelction.Visibility = Visibility.Visible;
blueRefelction.Visibility = Visibility.Visible;
}
}
}
}
ОБНОВИТЬ:
Я тестировал это еще несколько раз, чтобы попытаться выяснить, что является причиной проблемы, которую я вижу, и я считаю, что я нашел, что вызывает проблему.
Ниже я вставил новый xaml и код-позади. Новый образец ниже очень похож на оригинальный, с небольшими изменениями. XAML в основном состоит из двух стековых панелей, каждая из которых содержит две границы. Вторая граница на каждой панели стека - это визуальная кисть (отражение границы над ней). Теперь, когда я нажимаю кнопку "Перевернуть", у одной панели стека ScaleX уменьшается до нуля, а у второй панели стека, начальный ScaleX которой равен нулю, ScaleX увеличивается до 1. Эта анимация создает иллюзию переворота. Есть также два текстовых блока, которые отображают масштабный коэффициент каждой панели стека. Я добавил их, чтобы попытаться диагностировать мою проблему.
Проблема (как описано в оригинальном посте) в том, что анимация переключения не является плавной. Каждый раз, когда я нажимаю кнопку переворачивания, анимация запускается, но всякий раз, когда коэффициент ScaleX достигает примерно от.14 до.16, анимация выглядит так, как будто она останавливается, и на панелях стека никогда не уменьшается ScaleX до нуля, поэтому они никогда полностью не исчезают. Теперь странная вещь заключается в том, что если я изменю свойства Width/Height границ "frontBorder" и "backBorder", определенных ниже, чтобы использовать значения экспликации вместо Auto, такие как Width=105 и Height=75 (чтобы соответствовать кнопке в граница) все отлично работает. Анимация заикается первые два или три раза, когда я запускаю ее, но после этого сальто становятся плавными и безупречными. (Кстати, когда анимация запускается впервые, происходит ли что-то на заднем плане, какая-то инициализация, которая заставляет ее работать немного медленнее в первый раз?)
Возможно ли, что Авто Ширина / Высота границ вызывают проблему? Я могу воспроизвести его каждый раз, но я не уверен, почему Auto Width/Height будет проблемой.
Ниже приведен образец. Спасибо за помощь.
<Window x:Class="FlipTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<Storyboard x:Key="sbFlip">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="front" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00.5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00.5" Storyboard.TargetName="back" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00.5" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="sbFlipBack">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="back" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00.5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00.5" Storyboard.TargetName="front" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00.5" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel x:Name="front" RenderTransformOrigin="0.5,0.5">
<StackPanel.RenderTransform>
<ScaleTransform/>
</StackPanel.RenderTransform>
<Border Name="frontBorder" BorderBrush="Yellow" BorderThickness="2" Width="Auto" Height="Auto">
<Button Margin="0" Name="redButton" Height="75" Background="Red" Width="105" Click="FlipButton_Click"/>
</Border>
<Border Width="{Binding ElementName=frontBorder, Path=ActualWidth}"
Height="{Binding ElementName=frontBorder, Path=ActualHeight}"
Opacity="0.2" BorderBrush="Transparent">
<Border.OpacityMask>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0" Color="Black"/>
<GradientStop Offset=".6" Color="Transparent"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.OpacityMask>
<Border.Background>
<VisualBrush Visual="{Binding ElementName=frontBorder}">
<VisualBrush.Transform>
<ScaleTransform ScaleX="1" ScaleY="-1"
CenterX="52.5"
CenterY="37.5" />
</VisualBrush.Transform>
</VisualBrush>
</Border.Background>
</Border>
</StackPanel>
<StackPanel x:Name="back" RenderTransformOrigin="0.5,0.5">
<StackPanel.RenderTransform>
<ScaleTransform ScaleX="0"/>
</StackPanel.RenderTransform>
<Border Name="backBorder" BorderBrush="Yellow" BorderThickness="2" Width="Auto" Height="Auto">
<Button Margin="0" Width="105" Background="Blue" Name="blueButton" Height="75" Click="FlipButton_Click"/>
</Border>
<Border Width="{Binding ElementName=backBorder, Path=ActualWidth}"
Height="{Binding ElementName=backBorder, Path=ActualHeight}"
Opacity="0.2" BorderBrush="Transparent">
<Border.OpacityMask>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0" Color="Black"/>
<GradientStop Offset=".6" Color="Transparent"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.OpacityMask>
<Border.Background>
<VisualBrush Visual="{Binding ElementName=backBorder}">
<VisualBrush.Transform>
<ScaleTransform ScaleX="1" ScaleY="-1"
CenterX="52.5"
CenterY="37.5" />
</VisualBrush.Transform>
</VisualBrush>
</Border.Background>
</Border>
</StackPanel>
<Button Grid.Row="1" Click="FlipButton_Click" Height="19.45" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76">Flip</Button>
<TextBlock Grid.Row="2" Grid.Column="0" Foreground="DarkRed" Height="19.45" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76" Text="{Binding ElementName=front, Path=(UIElement.RenderTransform).(ScaleTransform.ScaleX)}"/>
<TextBlock Grid.Row="3" Grid.Column="0" Foreground="DarkBlue" Height="19.45" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76" Text="{Binding ElementName=back, Path=(UIElement.RenderTransform).(ScaleTransform.ScaleX)}"/>
</Grid>
</Window>
Код-за:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Animation;
namespace FlipTest
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
bool flipped = false;
private void FlipButton_Click(object sender, RoutedEventArgs e)
{
Storyboard sbFlip = (Storyboard)Resources["sbFlip"];
Storyboard sbFlipBack = (Storyboard)Resources["sbFlipBack"];
if (flipped)
{
sbFlipBack.Begin();
flipped = false;
}
else
{
sbFlip.Begin();
flipped = true;
}
}
}
}
1 ответ
Я попробовал это снова на моей новой машине x64, и анимация работала без проблем. То, что я видел раньше, должно быть связано с плохой работой моей старой машины.