c# - ملء Ellipse مع موجة الرسوم المتحركة




xaml uwp (2)

قبل إعطائك الرمز ، ألقِ نظرة على هذه الصورة المتحركة أدناه لمحاولة فهم كيفية إنشاء هذا الرسم المتحرك.

من المنطقي ، أليس كذلك؟ كل ما علينا فعله هو إنشاء شكل مثل هذا ، وتحريكه للإزاحة X (ما لا نهاية) و Y (مستوى الماء) ، وأخيراً قم بتقطيعه بمقطع بيضاوي.

لذلك يجب أولاً استخدام Adobe Illustrator أو أدوات مشابهة لإنشاء هذا الشكل. في AI ، هناك تأثير متعرج (انظر لقطة أدناه) وهذا تماما لهذا الغرض. تحتاج فقط إلى التأكد من أن نقطة البداية هي في نفس موضع نقطة النهاية ، لذلك عندما تكرر الرسوم المتحركة ، ستشعر أنها لا تنتهي أبدًا.

ما هو مفقود حاليًا في UWP هو القدرة على UIElement بشكل غير مستطيل ، لذلك هنا يجب علينا تصدير هذا كملف png (وإلا سنقوم بتصديره على أنه svg واستخدام Path لعرضه).

أيضا للسبب نفسه ، يتطلب جزء لقطة الكثير من العمل. كما هو الحال في إجابة Jet Chopper ، هذا هو الكثير من التعليمات البرمجية فقط للحصول على surfaceBrush ! ناهيك عن أنك ستحتاج أيضًا إلى التعامل مع دورة حياة التطبيق وخسارتها يدويًا.

لحسن الحظ ، في تطبيق Creators Update (أي 15063 ) ، هناك واجهة برمجة تطبيقات جديدة تسمى LoadedImageSurface تقوم بإنشاء CompositionSurfaceBrush خلال صورة uri LoadedImageSurface . في مثال التعليمة البرمجية أدناه ، سترى أنني استخدم هذا ، وهو ما يعني ، إذا كنت تريد دعم الإصدارات القديمة من 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>

لقد وضعت كل شيء في هذا المشروع عينة وأدناه هو عرض حي. استمتع! :)

لقد قمت بإنشاء مجموعة بيانية في Windows Phone 8.1 Silverlight App و UWP على حد سواء ، وأردت ملؤه بموجات متحركة ، ولهذا الغرض ، أتابع هذا solution

ولكن من أجل WPF لذلك أنا غير قادر على استخدام بعض التحكم مثل "Visual Brush".

كنت أرغب في ملء القطع الناقص مع موجة مشابهة لهذا (تجاهل 50 ٪ في الصورة) -

وهنا هو بلدي الويكي

<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


لقد حققت هذا باستخدام حل بسيط:

Wave2.png عبارة عن موسعة (تم لصق الصورة وإضافتها إلى نهاية الصورة الأولى) لجعلها أطول.

يعمل الحل على تطبيقات WP8 / Store / UWP / Silverlight

<Border
        Background="White"
        VerticalAlignment="Center"
        HorizontalAlignment="Center"
        CornerRadius="10000"
        BorderBrush="Black"
        BorderThickness="5">
        <Grid>
            <Ellipse
                x:Name="ellipse"
                VerticalAlignment="Center"
                HorizontalAlignment="Center"
                Height="200"
                Width="200">
                <Ellipse.Fill>
                    <ImageBrush
                        x:Name="WaveImage"
                        Stretch="None"
                        ImageSource="wave2.png">
                        <ImageBrush.Transform>
                            <CompositeTransform
                                TranslateY="200"
                                TranslateX="299" />
                        </ImageBrush.Transform>
                    </ImageBrush>
                </Ellipse.Fill>
            </Ellipse>
            <TextBlock
                VerticalAlignment="Center"
                HorizontalAlignment="Center"
                Text="HUJ" />
        </Grid>
    </Border>

وهنا رمز الرسوم المتحركة:

<Storyboard
        x:Name="AnimateWave">
        <DoubleAnimationUsingKeyFrames
            RepeatBehavior="Forever"
            EnableDependentAnimation="True"
            Storyboard.TargetProperty="(Shape.Fill).(Brush.Transform).(CompositeTransform.TranslateX)"
            Storyboard.TargetName="ellipse">
            <EasingDoubleKeyFrame
                KeyTime="0:0:5"
                Value="-299" />
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>