Подтвердить что ты не робот

Эффект пульсации Android в WPF

Мне нравится новая анимация Androids, где вы касаетесь элемента управления (listviewitem, кнопка и т.д. и т.д.), и он делает аккуратную анимацию следующим образом:

enter image description here

Мне интересно, как это можно реализовать в глобальном масштабе для всех "настраиваемых" элементов управления в WPF.


Мне нужна помощь в том, как круги должны создаваться на элементе управления. Единственное, о чем я думал, это создать собственные пользовательские элементы управления для каждого другого элемента управления (кнопки, радиокнопки и т.д.), Где у меня есть родительский элемент для эллипса, а также сам исходный элемент управления.

<UserControl>
   <Grid MouseLeftButtonDown="handler">
      <Button/> <--- this would be the button which you normally would place
   </Grid >
</UserControl>

И в handler-method создайте эллипс в точке e.GetPosition(handler), используя свойства margin, а затем оставьте его. Это решение будет работать. Но было бы хлопот делать это для каждого контроля, на который я хотел бы повлиять. В основном что-то вроде этого:

void handler(object sender, MouseButtonEventArgs e)
{
   Grid parent = (Grid)sender;
   Ellipse ellipse = new Ellipse();
   ellipse.Height = 10; // would be animated
   ellipse.Width = 10; // would be animated

   Point p = e.GetPosition(parent);

   ellipse.Margin = new Thickness(p.X, p.Y, 0, 0);

   parent.Children.Add(ellipse);

   // do the animation parts to later remove the ellipse
}

Есть ли более чистый, более расширяемый способ размещения эллипсов на моих элементах управления, отличных от того, как я ранее демонстрировал, поскольку не все элементы управления поддерживают детей?

4b9b3361

Ответ 1

UPDATE: Эта проблема была настолько интересной для меня, что я ее реализовал. Вы можете найти его на моей странице Github: https://github.com/Domysee/WpfCustomControls. Существует несколько настраиваемых элементов управления, которые вы ищете, это RippleEffectDecorator.


Теперь я объясню, что я сделал:

Я создал настраиваемый элемент управления, который наследуется от ContentControl, RippleEffectDecorator. Он определяет дополнительное свойство зависимостей HighlightBackground, которое используется для фона после щелчка по элементу.

Контрольная таблица RippleEffectDecorator состоит из Grid, Ellipse и ContentPresenter.

<ControlTemplate TargetType="{x:Type l:RippleEffectDecorator}">
    <Grid x:Name="PART_grid" ClipToBounds="True" Background="{TemplateBinding Background}"
            Width="{Binding ElementName=PART_contentpresenter, Path=ActualWidth}"
            Height="{Binding ElementName=PART_contentpresenter, Path=ActualHeight}">
        <Ellipse x:Name="PART_ellipse"
                        Fill="{Binding Path=HighlightBackground, RelativeSource={RelativeSource TemplatedParent}}" 
                        Width="0" Height="{Binding Path=Width, RelativeSource={RelativeSource Self}}" 
                        HorizontalAlignment="Left" VerticalAlignment="Top"/>

        <ContentPresenter x:Name="PART_contentpresenter" />
    </Grid>
</ControlTemplate>

Я использовал Grid вместо Border, чтобы добавить несколько дочерних элементов (необходимо, чтобы Ellipse и ContentPresenter могли перекрываться). Эллипс связывает его свойство Height с его собственной шириной, так что это всегда круг.

Теперь к важной части: анимация.

Grid определяет в своих ресурсах Storyboard, который воспроизводится в каждом событии MouseDown.

<Storyboard x:Key="PART_animation" Storyboard.TargetName="PART_ellipse">
    <DoubleAnimation Storyboard.TargetProperty="Width" From="0" />
    <ThicknessAnimation Storyboard.TargetProperty="Margin" />
    <DoubleAnimation BeginTime="0:0:1" Duration="0:0:0.25" Storyboard.TargetProperty="Opacity"
                From="1" To="0" />
    <DoubleAnimation Storyboard.TargetProperty="Width" To="0" BeginTime="0:0:1.25" Duration="0:0:0" />
    <DoubleAnimation BeginTime="0:0:1.25" Duration="0:0:0" Storyboard.TargetProperty="Opacity" To="1" />
</Storyboard>

Раскадровка оживляет свойство ширины эллипса, чтобы оно полностью заполняло область. Он также должен анимировать маржу, потому что эллипс позиционирует себя относительно верхней левой точки (не вокруг ее центра).

Начальное положение эллипса, его ширина цели и его положение в контейнере во всем эффекте должны быть установлены программно. Я перезаписываю метод OnApplyTemplate(), чтобы добавить обработчик события в событие мыши, которое запускает раскадровку и устанавливает все необходимые значения.

public override void OnApplyTemplate()
{
    base.OnApplyTemplate();

    ellipse = GetTemplateChild("PART_ellipse") as Ellipse;
    grid = GetTemplateChild("PART_grid") as Grid;
    animation = grid.FindResource("PART_animation") as Storyboard;

    this.AddHandler(MouseDownEvent, new RoutedEventHandler((sender, e) =>
    {
        var targetWidth = Math.Max(ActualWidth, ActualHeight) * 2;
        var mousePosition = (e as MouseButtonEventArgs).GetPosition(this);
        var startMargin = new Thickness(mousePosition.X, mousePosition.Y, 0, 0);
        //set initial margin to mouse position
        ellipse.Margin = startMargin;
        //set the to value of the animation that animates the width to the target width
        (animation.Children[0] as DoubleAnimation).To = targetWidth;
        //set the to and from values of the animation that animates the distance relative to the container (grid)
        (animation.Children[1] as ThicknessAnimation).From = startMargin;
        (animation.Children[1] as ThicknessAnimation).To = new Thickness(mousePosition.X - targetWidth / 2, mousePosition.Y - targetWidth / 2, 0, 0);
        ellipse.BeginStoryboard(animation);
    }), true);
}

Примечание. последний параметр AddHandler() определяет, хотите ли вы получать обработанные события. Важно установить значение true, потому что некоторые UiElements обрабатывают события мыши (например, Button). В противном случае MouseDownEvent не будет запускаться, и поэтому анимация не будет выполнена.

Чтобы использовать его, просто добавьте элемент, на который вы хотите получить этот эффект, в качестве дочернего элемента RippleEffectDecorator и Background to Transparent:

<cc:RippleEffectDecorator Background="Green" HighlightBackground="LightGreen">
    <Button FontSize="60" Background="Transparent">stuff</Button>
</cc:RippleEffectDecorator>

Примечание2: некоторые элементы включают триггеры, которые устанавливают шаблон на MouseOver (например, Button) и, следовательно, скрывают эффект. Если вы не хотите, чтобы вы установили шаблон кнопки и удалили эти триггеры. Самый простой способ - использовать Blend, получить шаблон кнопки из него, удалить все триггеры и добавить его в качестве шаблона вашей кнопки.

Ответ 3

Я предлагаю использовать для этого пользовательские элементы управления через UserControls. Практически все можно было бы обработать в xaml. После того, как у вас есть свой стиль управления, все, что вам нужно сделать, это добавить Ellipse и установить триггер MouseDown или Button.Pressed на ваш ControlTemplate.Triggers. Тогда вашей анимации нужно будет только увеличить высоту и ширину эллипса до тех пор, пока эффект пульсации не будет завершен, а затем уменьшите Opacity до 0.

Для Ellipse убедитесь, что у вас есть фиксированная позиция и для той же цветовой схемы попробуйте белую заливку и непрозрачность 0.3-5.

Для эффекта кольца на кнопке в верхнем углу вашего добавленного 2-го эллипса установите Fill to Transparent и Stroke to White.