用波浪动画填充椭圆 [英] Fill Ellipse with wave animation

查看:71
本文介绍了用波浪动画填充椭圆的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Windows Phone 8.1 Silverlight App和UWP中都创建了一个椭圆我想用动画波填充它,为此,我正在遵循此

这是我的遗物

 <椭圆名称="WaveEllipse" Grid.Column ="1" Grid.Row ="0" VerticalAlignment ="Top"描边="{StaticResource PhoneAccentBrush}"StrokeThickness ="4"宽度="225"身高="225"></椭圆> 

视觉笔刷上有其他选择吗?主要是我想在Windows Phone 8.1 Silverlight中实现它,但是如果WP平台上不提供UWP,我将切换到UWP

解决方案

在给您代码之前,请看下面的动画 gif ,以尝试了解如何创建此动画./p>

有道理吧?我们要做的就是创建一个这样的形状,为其偏移量X(无限地)和Y(水位)设置动画,最后用椭圆对其进行裁剪.

因此,首先您需要使用 Adob​​e Illustrator 或类似工具来创建此形状.在 AI 中,有一个 Zig Zag 效果(请参见下面的屏幕截图),非常适合此效果.您只需要确保起点与终点在同一位置即可,因此,当您重复动画时,感觉就像永无止境.

UWP当前缺少的功能是剪切具有非矩形形状的 UIElement 的功能,因此在这里我们必须将其导出为 png (否则我们将将其导出为 svg 并使用 Path 进行显示).

出于同样的原因,剪辑部分也​​需要大量工作.就像在Jet Chopper的答案中一样,要获得 surfaceBrush 就是很多代码!更不用说您还需要手动处理设备丢失和应用程序生命周期.

值得庆幸的是,在Creators Update(即 15063 )中,有一个名为 LoadedImageSurface 的新API,它可以通过图像uri创建一个 CompositionSurfaceBrush ,几行代码.在下面的代码示例中,您会看到我使用了它,这意味着,如果要支持Windows 10的较早版本,则需要用Jet的答案替换它.

代码

想法是创建一个名为 WaveProgressControl 的UserControl,该UserControl封装所有动画逻辑,并公开一个名为 Percent 的依赖项属性,该属性控制水位.

WaveProgressControl 控件-XAML

 < UserControl x:Class ="WaveProgressControlRepo.WaveProgressControl"身高="160"宽度="160">< Grid x:Name ="Root">< Ellipse x:Name ="ClippedImageContainer"填充=白色"Margin ="6"/><椭圆x:Name ="CircleBorder"描边=#FF0289CD"StrokeThickness ="3"/>< TextBlock Foreground =#FF0289CD"FontSize ="36"FontWeight ="SemiBold"TextAlignment =右"VerticalAlignment ="Center"宽度="83"Margin ="0,0,12,0"><运行文本="{x:绑定百分比,模式=单向}"/>< Run Text =%"FontSize ="22"/></TextBlock></Grid></UserControl> 

WaveProgressControl 控件-代码隐藏

  private只读Compositor _compositor;私有只读CompositionPropertySet _percentPropertySet;公共WaveProgressControl(){InitializeComponent();_compositor = Window.Current.Compositor;_percentPropertySet = _compositor.CreatePropertySet();_percentPropertySet.InsertScalar("Value",0.0f);已加载+ =已加载;}公众双倍百分比{得到=>(double)GetValue(PercentProperty);设置=>SetValue(PercentProperty,value);}公共静态只读DependencyProperty PercentProperty =DependencyProperty.Register("Percent",typeof(double),typeof(WaveProgressControl),new PropertyMetadata(0.0d,(s,e)=>{var self =(WaveProgressControl)s;var propertySet = self._percentPropertySet;propertySet.InsertScalar("Value",Convert.ToSingle(e.NewValue)/100);}));私有void OnLoaded(对象发送者,RoutedEventArgs e){CompositionSurfaceBrush imageSurfaceBrush;SetupClippedWaveImage();SetupEndlessWaveAnimationOnXAxis();SetupExpressionAnimationOnYAxisBasedOnPercentValue();void SetupClippedWaveImage(){//注意LoadedImageSurface仅在15063起可用.var imageSurface = LoadedImageSurface.StartLoadFromUri(new Uri(BaseUri,"/Assets/wave.png"));imageSurfaceBrush = _compositor.CreateSurfaceBrush(imageSurface);imageSurfaceBrush.Stretch = CompositionStretch.None;imageSurfaceBrush.Offset =新的Vector2(120,248);var maskBrush = _compositor.CreateMaskBrush();var maskSurfaceBrush = ClippedImageContainer.GetAlphaMask();//CompositionSurfaceBrushmaskBrush.Mask = maskSurfaceBrush;maskBrush.Source = imageSurfaceBrush;var imageVisual = _compositor.CreateSpriteVisual();imageVisual.RelativeSizeAdjustment = Vector2.One;ElementCompositionPreview.SetElementChildVisual(ClippedImageContainer,imageVisual);imageVisual.Brush = maskBrush;}void SetupEndlessWaveAnimationOnXAxis(){var waveOffsetXAnimation = _compositor.CreateScalarKeyFrameAnimation();waveOffsetXAnimation.InsertKeyFrame(1.0f,-80.0f,_compositor.CreateLinearEasingFunction());waveOffsetXAnimation.Duration = TimeSpan.FromSeconds(1);waveOffsetXAnimation.IterationBehavior = AnimationIterationBehavior.Forever;imageSurfaceBrush.StartAnimation("Offset.X",waveOffsetXAnimation);}void SetupExpressionAnimationOnYAxisBasedOnPercentValue(){var waveOffsetYExpressionAnimation = _compositor.CreateExpressionAnimation("Lerp(248.0f,120.0f,Percent.Value)");waveOffsetYExpressionAnimation.SetReferenceParameter("Percent",_percentPropertySet);imageSurfaceBrush.StartAnimation("Offset.Y",waveOffsetYExpressionAnimation);}} 

主页

 <网格背景="{ThemeResource ApplicationPageBackgroundThemeBrush}">< Grid.RowDefinitions><行定义/>< RowDefinition Height ="Auto"/></Grid.RowDefinitions>< local:WaveProgressControl x:Name ="WaveProgressControl"/>< Slider Grid.Row ="1"Margin ="24"Value ="{x:Bind WaveProgressControl.Percent,Mode = TwoWay}"/></Grid> 

我已经将所有内容都放入了

I have created an ellipse in Windows Phone 8.1 Silverlight App and UWP both and I wanted to fill it with animating waves, For this purpose, I am following this solution

but it is for WPF so I am unable to use some control like "Visual Brush".

I wanted to fill ellipse with wave similar to this (ignore 50% in the image) -

And here is my eliipse

<Ellipse Name="WaveEllipse" Grid.Column="1" Grid.Row="0" VerticalAlignment="Top"
         Stroke="{StaticResource PhoneAccentBrush}"
         StrokeThickness="4"
         Width="225"
         Height="225">
</Ellipse>

any alternate on the visual brush? mainly I wanted to implement it in Windows Phone 8.1 Silverlight, but I will switch to UWP if it is not available on WP platform

解决方案

Before giving you the code, have a look at this animated gif below to try to understand how this animation could be created.

Make sense, right? All we need to do is to create a shape like this, animate its offset X(endlessly) and Y(water level), and finally just clip it with an ellipse.

So first you will need to use Adobe Illustrator or similar tools to create this shape. In AI, there's a Zig Zag effect(see screenshot below) that's perfectly for this. You just need to make sure the starting point is at the same position as the ending one, so when you repeat the animation, it will feel like it's never ending.

What's currently missing in UWP is the ability to clip a UIElement with a non-rectangular shape, so here we have to export this as a png (otherwise we would export it as a svg and use Path to display it).

Also for the same reason, the clipping part requires a lot of work. Like in Jet Chopper's answer, that's tons of code to just get a surfaceBrush! Not to mention that you will also need to manually handle device lost and app lifecycle.

Thankfully, in Creators Update(i.e. 15063), there's a new API called LoadedImageSurface that creates a CompositionSurfaceBrush by an image uri with a couple of lines' code. In my code example below, you will see that I use this, which means, if you want to support older versions of Windows 10, you will need to replace it with what's in Jet's answer.

Code

The idea is to create a UserControl called WaveProgressControl which encapsulates all the animation logic and exposes a dependency property called Percent that controls the water level.

The WaveProgressControl control - XAML

<UserControl x:Class="WaveProgressControlRepo.WaveProgressControl"
             Height="160"
             Width="160">

    <Grid x:Name="Root">
        <Ellipse x:Name="ClippedImageContainer"
                 Fill="White"
                 Margin="6" />
        <Ellipse x:Name="CircleBorder"
                 Stroke="#FF0289CD"
                 StrokeThickness="3" />
        <TextBlock Foreground="#FF0289CD"
                   FontSize="36"
                   FontWeight="SemiBold"
                   TextAlignment="Right"
                   VerticalAlignment="Center"
                   Width="83"
                   Margin="0,0,12,0">
            <Run Text="{x:Bind Percent, Mode=OneWay}" />
            <Run Text="%"
                 FontSize="22" />
        </TextBlock>
    </Grid>
</UserControl>

The WaveProgressControl control - Code-behind

private readonly Compositor _compositor;
private readonly CompositionPropertySet _percentPropertySet;

public WaveProgressControl()
{
    InitializeComponent();

    _compositor = Window.Current.Compositor;

    _percentPropertySet = _compositor.CreatePropertySet();
    _percentPropertySet.InsertScalar("Value", 0.0f);

    Loaded += OnLoaded;
}

public double Percent
{
    get => (double)GetValue(PercentProperty);
    set => SetValue(PercentProperty, value);
}
public static readonly DependencyProperty PercentProperty =
    DependencyProperty.Register("Percent", typeof(double), typeof(WaveProgressControl),
        new PropertyMetadata(0.0d, (s, e) =>
        {
            var self = (WaveProgressControl)s;
            var propertySet = self._percentPropertySet;
            propertySet.InsertScalar("Value", Convert.ToSingle(e.NewValue) / 100);
        }));

private void OnLoaded(object sender, RoutedEventArgs e)
{
    CompositionSurfaceBrush imageSurfaceBrush;

    SetupClippedWaveImage();
    SetupEndlessWaveAnimationOnXAxis();
    SetupExpressionAnimationOnYAxisBasedOnPercentValue();

    void SetupClippedWaveImage()
    {
        // Note LoadedImageSurface is only available in 15063 onward.
        var imageSurface = LoadedImageSurface.StartLoadFromUri(new Uri(BaseUri, "/Assets/wave.png"));
        imageSurfaceBrush = _compositor.CreateSurfaceBrush(imageSurface);
        imageSurfaceBrush.Stretch = CompositionStretch.None;
        imageSurfaceBrush.Offset = new Vector2(120, 248);

        var maskBrush = _compositor.CreateMaskBrush();
        var maskSurfaceBrush = ClippedImageContainer.GetAlphaMask(); // CompositionSurfaceBrush
        maskBrush.Mask = maskSurfaceBrush;
        maskBrush.Source = imageSurfaceBrush;

        var imageVisual = _compositor.CreateSpriteVisual();
        imageVisual.RelativeSizeAdjustment = Vector2.One;
        ElementCompositionPreview.SetElementChildVisual(ClippedImageContainer, imageVisual);

        imageVisual.Brush = maskBrush;
    }

    void SetupEndlessWaveAnimationOnXAxis()
    {
        var waveOffsetXAnimation = _compositor.CreateScalarKeyFrameAnimation();
        waveOffsetXAnimation.InsertKeyFrame(1.0f, -80.0f, _compositor.CreateLinearEasingFunction());
        waveOffsetXAnimation.Duration = TimeSpan.FromSeconds(1);
        waveOffsetXAnimation.IterationBehavior = AnimationIterationBehavior.Forever;
        imageSurfaceBrush.StartAnimation("Offset.X", waveOffsetXAnimation);
    }

    void SetupExpressionAnimationOnYAxisBasedOnPercentValue()
    {
        var waveOffsetYExpressionAnimation = _compositor.CreateExpressionAnimation("Lerp(248.0f, 120.0f, Percent.Value)");
        waveOffsetYExpressionAnimation.SetReferenceParameter("Percent", _percentPropertySet);
        imageSurfaceBrush.StartAnimation("Offset.Y", waveOffsetYExpressionAnimation);
    }
}

The MainPage

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <local:WaveProgressControl x:Name="WaveProgressControl" />

    <Slider Grid.Row="1"
            Margin="24"
            Value="{x:Bind WaveProgressControl.Percent, Mode=TwoWay}" />
</Grid>

I have put everything into this sample project and below is a live demo. Enjoy! :)

这篇关于用波浪动画填充椭圆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆