Avalonia 11.1 已知問題 IterationCount 為 Infinite 的動畫播放出現異常

lindexi發表於2024-08-23

如果在 Avalonia 後臺程式碼播放一個動畫,這個動畫的 Animation 的 IterationCount 被設定為 Infinite 那麼將在播放的時候丟擲 InvalidOperationException 異常

本文所使用的 Avalonia 為 11.1.0 版本,由於 Avalonia 行為和 API 變動較多,如大家使用其他版本還請重新測試

如以下程式碼,將會丟擲 InvalidOperationException 異常

        var content = Content;
        var textBlock = (TextBlock) content!;

        var animation = new Animation()
        {
            Duration = TimeSpan.FromSeconds(10),
            IterationCount = IterationCount.Infinite,
            PlaybackDirection = PlaybackDirection.Alternate,
            Children =
            {
                new KeyFrame()
                {
                    Setters =
                    {
                        new Setter(TranslateTransform.XProperty, 0d),
                    },
                    KeyTime = TimeSpan.FromSeconds(0)
                },
                new KeyFrame()
                {
                    Setters =
                    {
                        new Setter(TranslateTransform.XProperty, 500d),
                    },
                    KeyTime = TimeSpan.FromSeconds(10)
                }
            }
        };

        textBlock.RenderTransform = new TranslateTransform();

        await animation.RunAsync(textBlock);

以上程式碼是從 Avalonia 後臺程式碼簡單播放動畫示例 修改的

執行以上程式碼,將可以看到如下錯誤資訊

System.InvalidOperationException:“Looping animations must not use the Run method.”

經過閱讀 Avalonia 的程式碼,我理解了 Avalonia 的意圖。在 Avalonia 裡面,認為如果一個動畫是無限播放的,那就不應該使用 RunAsync 等待,防止等待的邏輯永不返回

然而 Avalonia 卻允許呼叫 RunAsync 方法播放動畫,換句話說就是隻要使用 RunAsync 而不進行等待就不會炸掉

只需將 await animation.RunAsync(textBlock); 修改為 _ = animation.RunAsync(textBlock); 即可

如果即可執行一個無限播放的動畫

我認為這是 Avalonia 的設計問題,但是不太確定,於是就在 Avalonia 提了一個討論

詳細請看 https://github.com/AvaloniaUI/Avalonia/discussions/16757

為什麼不等待 RunAsync 也可以?這是因為在 RunAsync 裡面是採用 TaskCompletionSource 作為等待機制,判斷如果是無限迴圈的動畫,則設定 TaskCompletionSource 異常而已

            var run = new TaskCompletionSource<object?>();

            if (this.IterationCount == IterationCount.Infinite)
                run.SetException(new InvalidOperationException("Looping animations must not use the Run method."));

這就意味著在不等待 RunAsync 時,將只是 TaskCompletionSource 被設定異常狀態,不會真的出現任何異常。也就是儘管 InvalidOperationException 被 new 了,但是沒有地方丟擲

本文程式碼放在 githubgitee 上,可以使用如下命令列拉取程式碼。我整個程式碼倉庫比較龐大,使用以下命令列可以進行部分拉取,拉取速度比較快

先建立一個空資料夾,接著使用命令列 cd 命令進入此空資料夾,在命令列裡面輸入以下程式碼,即可獲取到本文的程式碼

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 2b7efe38fb648e446b13d9449c92ae5bd4e7cd9a

以上使用的是國內的 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源。請在命令列繼續輸入以下程式碼,將 gitee 源換成 github 源進行拉取程式碼。如果依然拉取不到程式碼,可以發郵件向我要程式碼

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 2b7efe38fb648e446b13d9449c92ae5bd4e7cd9a

獲取程式碼之後,進入 AvaloniaIDemo/LicajearyaWenewernichiji 資料夾,即可獲取到原始碼

更多技術部落格,請參閱 部落格導航

相關文章