亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

WPF實現(xiàn)類似ChatGPT逐字打印效果的示例代碼

 更新時間:2023年08月11日 09:54:03   作者:czwy  
前一段時間ChatGPT類的應(yīng)用十分火爆,這類應(yīng)用在回答用戶的問題時逐字打印輸出,像極了真人打字回復(fù)消息,本文就來利用WPF模擬一下這種逐字打印的效果吧

背景

前一段時間ChatGPT類的應(yīng)用十分火爆,這類應(yīng)用在回答用戶的問題時逐字打印輸出,像極了真人打字回復(fù)消息。出于對這個效果的興趣,決定用WPF模擬這個效果。

真實的ChatGPT逐字輸出效果涉及其語言生成模型原理以及服務(wù)端與前端通信機制,本文不做過多闡述,重點是如何用WPF模擬這個效果。

技術(shù)要點與實現(xiàn)

對于這個逐字輸出的效果,我想到了兩種實現(xiàn)方法:

方法一:根據(jù)字符串長度n,添加n個關(guān)鍵幀DiscreteStringKeyFrame,第一幀的Value為字符串的第一個字符,緊接著的關(guān)鍵幀都比上一幀的Value多一個字符,直到最后一幀的Value是完整的目標(biāo)字符串。實現(xiàn)效果如下所示:

方法二:首先把TextBlock的字體顏色設(shè)置為透明,然后通過TextEffectPositionStartPositionCount屬性控制應(yīng)用動畫效果的子字符串的起始位置以及長度,同時使用ColorAnimation設(shè)置TextEffectForeground屬性由透明變?yōu)槟繕?biāo)顏色(假定是黑色)。實現(xiàn)效果如下所示:

由于方案二的思路與WPF實現(xiàn)跳動的字符效果中的效果實現(xiàn)思路非常類似,具體實現(xiàn)不再詳述。接下來我們看一下方案一通過關(guān)鍵幀動畫拼接字符串的具體實現(xiàn)。

public class TypingCharAnimationBehavior : Behavior<TextBlock>
{
    private Storyboard _storyboard;
    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.Loaded += AssociatedObject_Loaded; ;
        this.AssociatedObject.Unloaded += AssociatedObject_Unloaded;
        BindingOperations.SetBinding(this, TypingCharAnimationBehavior.InternalTextProperty, new Binding("Tag") { Source = this.AssociatedObject });
    }
    private void AssociatedObject_Unloaded(object sender, RoutedEventArgs e)
    {
        StopEffect();
    }
    private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
    {
        if (IsEnabled)
            BeginEffect(InternalText);
    }
    protected override void OnDetaching()
    {
        base.OnDetaching();
        this.AssociatedObject.Loaded -= AssociatedObject_Loaded;
        this.AssociatedObject.Unloaded -= AssociatedObject_Unloaded;
        this.ClearValue(TypingCharAnimationBehavior.InternalTextProperty);
        if (_storyboard != null)
        {
            _storyboard.Remove(this.AssociatedObject);
            _storyboard.Children.Clear();
        }
    }
    private string InternalText
    {
        get { return (string)GetValue(InternalTextProperty); }
        set { SetValue(InternalTextProperty, value); }
    }
    private static readonly DependencyProperty InternalTextProperty =
    DependencyProperty.Register("InternalText", typeof(string), typeof(TypingCharAnimationBehavior),
    new PropertyMetadata(OnInternalTextChanged));
    private static void OnInternalTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var source = d as TypingCharAnimationBehavior;
        if (source._storyboard != null)
        {
            source._storyboard.Stop(source.AssociatedObject);
            source._storyboard.Children.Clear();
        }
        source.SetEffect(e.NewValue == null ? string.Empty : e.NewValue.ToString());
    }
    public bool IsEnabled
    {
        get { return (bool)GetValue(IsEnabledProperty); }
        set { SetValue(IsEnabledProperty, value); }
    }
    public static readonly DependencyProperty IsEnabledProperty =
        DependencyProperty.Register("IsEnabled", typeof(bool), typeof(TypingCharAnimationBehavior), new PropertyMetadata(true, (d, e) =>
        {
            bool b = (bool)e.NewValue;
            var source = d as TypingCharAnimationBehavior;
            source.SetEffect(source.InternalText);
        }));
    private void SetEffect(string text)
    {
        if (string.IsNullOrEmpty(text) || this.AssociatedObject.IsLoaded == false)
        {
            StopEffect();
            return;
        }
        BeginEffect(text);
    }
    private void StopEffect()
    {
        if (_storyboard != null)
        {
            _storyboard.Stop(this.AssociatedObject);
        }
    }
    private void BeginEffect(string text)
    {
        StopEffect();
        int textLength = text.Length;
        if (textLength < 1  || IsEnabled == false) return;
        if (_storyboard == null)
            _storyboard = new Storyboard();
        double duration = 0.15d;
        StringAnimationUsingKeyFrames frames = new StringAnimationUsingKeyFrames();
        Storyboard.SetTargetProperty(frames, new PropertyPath(TextBlock.TextProperty));
        frames.Duration = TimeSpan.FromSeconds(textLength * duration);
        for(int i=0;i<textLength;i++)
        {
            frames.KeyFrames.Add(new DiscreteStringKeyFrame()
            {
                Value = text.Substring(0,i+1),
                KeyTime = TimeSpan.FromSeconds(i * duration),
            });
        }
        _storyboard.Children.Add(frames);
        _storyboard.Begin(this.AssociatedObject, true);
    }
}

由于每一幀都在修改TextBlockText屬性的值,如果TypingCharAnimationBehavior直接綁定TextBlockText屬性,當(dāng)Text屬性的數(shù)據(jù)源發(fā)生變化時,無法判斷是關(guān)鍵幀動畫修改的,還是外部數(shù)據(jù)源變化導(dǎo)致Text的值被修改。因此這里用TextBlockTag屬性暫存要顯示的字符串內(nèi)容。調(diào)用的時候只需要把需要顯示的字符串變量綁定到Tag,并在TextBlock添加Behavior即可,代碼如下:

<TextBlock x:Name="source"
            IsEnabled="True"
            Tag="{Binding TypingText, ElementName=self}"
            TextWrapping="Wrap">
    <i:Interaction.Behaviors>
        <local:TypingCharAnimationBehavior IsEnabled="True" />
    </i:Interaction.Behaviors>
</TextBlock>

小結(jié)

兩種方案各有利弊:

關(guān)鍵幀動畫拼接字符串這個方法的優(yōu)點是最大程度還原了逐字輸出的過程,缺點是需要額外的屬性來輔助,另外遇到英文單詞換行時,會出現(xiàn)單詞從上一行行尾跳到下一行行首的問題;

通過TextEffect設(shè)置字體顏色這個方法則相反,不需要額外的屬性輔助,并且不會出現(xiàn)單詞在輸入過程中從行尾跳到下一行行首的問題,開篇中兩種實現(xiàn)方法效果圖中能看出這一細微差異。但是一開始就把文字都渲染到界面上,只是通過透明的字體顏色騙過用戶的眼睛,逐字改變字體顏色模擬逐字打印的效果。

到此這篇關(guān)于WPF實現(xiàn)類似ChatGPT逐字打印效果的示例代碼的文章就介紹到這了,更多相關(guān)WPF逐字打印效果內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C#文件目錄操作方法匯總

    C#文件目錄操作方法匯總

    本文主要列舉出C#文件和目錄操作的一些方法,包括創(chuàng)建、移動、遍歷目錄,讀寫文件等方法,有需要的小伙伴可以學(xué)習(xí)一下。
    2016-04-04
  • WPF快速入門教程之綁定Binding

    WPF快速入門教程之綁定Binding

    初學(xué)wpf,經(jīng)常被Binding搞暈,以下記錄寫B(tài)inding的基礎(chǔ)。下面這篇文章主要給大家介紹了關(guān)于WPF快速入門教程之綁定Binding的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2018-10-10
  • C# 使用動態(tài)庫DllImport("kernel32")讀寫ini文件的步驟

    C# 使用動態(tài)庫DllImport("kernel32")讀寫ini文件的步驟

    kernel32.dll是Windows中非常重要的32位動態(tài)鏈接庫文件,屬于內(nèi)核級文件,這篇文章主要介紹了C# 利用動態(tài)庫DllImport("kernel32")讀寫ini文件,需要的朋友可以參考下
    2023-05-05
  • 新手學(xué)習(xí).net的一列好走的路徑及方法

    新手學(xué)習(xí).net的一列好走的路徑及方法

    新手學(xué)習(xí).net的一列好走的路徑及方法,想學(xué)習(xí).net的朋友可以參考下。
    2011-11-11
  • C#配置文件設(shè)置及應(yīng)用詳解

    C#配置文件設(shè)置及應(yīng)用詳解

    在軟件開發(fā)過程中,配置文件是常用的一個功能,用于在程序運行時調(diào)整應(yīng)用程序的行為,C# 提供了多種方式來創(chuàng)建和使用配置文件,本文將詳細介紹 C# 配置文件的創(chuàng)建、修改、讀取和寫入,以及跨平臺配置文件的應(yīng)用,需要的朋友可以參考下
    2024-06-06
  • 如何在C#9 中使用static匿名函數(shù)

    如何在C#9 中使用static匿名函數(shù)

    這篇文章主要介紹了如何在C#9中使用static匿名函數(shù),幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下
    2021-03-03
  • VS2017使用Git進行源代碼管理的實現(xiàn)

    VS2017使用Git進行源代碼管理的實現(xiàn)

    這篇文章主要介紹了VS2017使用Git進行源代碼管理的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • 用C#編寫ActiveX控件(二)

    用C#編寫ActiveX控件(二)

    用C#編寫ActiveX控件(二)...
    2007-03-03
  • vs2022程序打包文檔教程圖文詳解

    vs2022程序打包文檔教程圖文詳解

    這篇文章主要介紹了vs2022程序打包文檔教程,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-10-10
  • C# DataTable.Select()根據(jù)條件篩選數(shù)據(jù)問題

    C# DataTable.Select()根據(jù)條件篩選數(shù)據(jù)問題

    這篇文章主要介紹了C# DataTable.Select()根據(jù)條件篩選數(shù)據(jù)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01

最新評論