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

Заполнить эллипс волновой анимацией

Я создал эллипс в Windows Phone 8.1 Silverlight App и UWP как и я хотел заполнить его анимированными волнами, Для этого я следую этому решению

но это для WPF, поэтому я не могу использовать какой-то элемент управления, например "Visual Brush".

Я хотел заполнить эллипс волной, подобной этому (игнорировать 50% на изображении) -

введите описание изображения здесь

И вот мой eliipse

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

любая альтернатива на визуальной кисти? в основном я хотел реализовать его в Windows Phone 8.1 Silverlight, но я переключусь на UWP, если он недоступен на платформе WP.

4b9b3361

Ответ 1

Прежде чем предоставить вам код, посмотрите на этот анимированный gif ниже, чтобы попытаться понять, как можно создать эту анимацию.

введите описание изображения здесь

Имеют смысл, не так ли? Все, что нам нужно сделать, это создать такую ​​форму, анимировать ее смещение X (бесконечно) и Y (уровень воды), и, наконец, просто закрепить его эллипсом.

Итак, сначала вам нужно будет использовать Adobe Illustrator или аналогичные инструменты для создания этой формы. В AI есть эффект Zig Zag (см. Снимок экрана ниже), который идеально подходит для этого. Вам просто нужно убедиться, что начальная точка находится в том же положении, что и конечный, поэтому, когда вы повторяете анимацию, она будет чувствовать, что она никогда не заканчивается.

введите описание изображения здесь

То, что в настоящее время отсутствует в UWP, - это возможность клипа UIElement с непрямоугольной формой, поэтому здесь мы должны экспортировать это как png (в противном случае мы будем экспортировать его как svg и использовать Path для отображения она).

По той же причине часть отсечения требует большой работы. Как и в Jet Chopper, ответьте, что тонны кода просто получите surfaceBrush! Не говоря уже о том, что вам также потребуется вручную управлять потерянным устройством и жизненным циклом приложения.

К счастью, в Creators Update (т.е. 15063) появился новый API под названием LoadedImageSurface, который создает CompositionSurfaceBrush образ uri с паролем строк. В приведенном ниже примере кода вы увидите, что я использую это, а это значит, что если вы хотите поддерживать более старые версии Windows 10, вам нужно будет заменить его тем, что ответит Jet.

код

Идея состоит в создании UserControl под названием WaveProgressControl, который инкапсулирует всю логику анимации и предоставляет свойство зависимостей, называемое Percent, которое контролирует уровень воды.

Элемент управления WaveProgressControl - 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>

Элемент управления WaveProgressControl - Код

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);
    }
}

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>

Я поместил все в этот образец проекта, а ниже - живая демонстрация. Наслаждайтесь!:)

введите описание изображения здесь

Ответ 2

Здесь образец UWP. Вы можете настроить его, как хотите:

<Canvas>
    <Ellipse x:Name="Ellipse" Width="256" Height="256" Fill="DarkViolet" Stroke="DeepSkyBlue" StrokeThickness="8"/>
    <Border x:Name="VisualBorder" Opacity="0.5"/>
</Canvas>

И код позади:

    private async void CreateVisuals()
    {
        var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;

        var bitmap = await CanvasBitmap.LoadAsync(CanvasDevice.GetSharedDevice(),
            new Uri("ms-appx:///Assets/Wave-PNG-Transparent-Picture.png"));

        var drawingSurface =
            CanvasComposition.CreateCompositionGraphicsDevice(compositor, CanvasDevice.GetSharedDevice())
                .CreateDrawingSurface(bitmap.Size,
                    DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied);
        using (var ds = CanvasComposition.CreateDrawingSession(drawingSurface))
        {
            ds.Clear(Colors.Transparent);
            ds.DrawImage(bitmap);
        }

        var surfaceBrush = compositor.CreateSurfaceBrush(drawingSurface);
        surfaceBrush.Stretch = CompositionStretch.None;

        var maskedBrush = compositor.CreateMaskBrush();
        maskedBrush.Mask = Ellipse.GetAlphaMask();
        maskedBrush.Source = surfaceBrush;

        var sprite = compositor.CreateSpriteVisual();
        sprite.Size = new Vector2((float)Ellipse.Width, (float)Ellipse.Height);
        sprite.Brush = maskedBrush;
        sprite.CenterPoint = new Vector3(sprite.Size / 2, 0);
        sprite.Scale = new Vector3(0.9f);

        ElementCompositionPreview.SetElementChildVisual(VisualBorder, sprite);

        var offsetAnimation = compositor.CreateScalarKeyFrameAnimation();
        offsetAnimation.InsertKeyFrame(0, 0);
        offsetAnimation.InsertKeyFrame(1, 256, compositor.CreateLinearEasingFunction());
        offsetAnimation.Duration = TimeSpan.FromMilliseconds(1000);
        offsetAnimation.IterationBehavior = AnimationIterationBehavior.Forever;

        surfaceBrush.StartAnimation("Offset.X", offsetAnimation);
    }
}

Вот как это выглядит:

введите описание изображения здесь