基于WPF實現(xiàn)跳動的字符效果
本文將介紹一個好玩但實際作用可能不太大的動畫效果:跳動的字符。為了提高動畫效果的可重用性以及調(diào)用的靈活性,通過Behavior實現(xiàn)跳動的字符動畫。先看下效果:
技術(shù)要點與實現(xiàn)
通過TextEffect
的PositionStart
和PositionCount
屬性控制應用動畫效果的子字符串的起始位置以及長度,同時使用TranslateTransform
設置字符縱坐標的移動變換,以實現(xiàn)跳動的效果。主要步驟如下:
- 在OnAttached方法中,注冊
Loaded
事件,在Load
事件中為TextBlock
添加TextEffect
效果,其中PositionCount
設置為1,每次只跳動一個字符。 - 添加啟動動畫效果的
BeginEffect
方法,并創(chuàng)建控制子字符縱向移動變換的線性動畫。然后根據(jù)字符串(剔除空字符)的長度n,創(chuàng)建n個關(guān)鍵幀,每個關(guān)鍵幀中把PositionStart
設置為要跳動的字符在字符串中的索引 - 在開啟動畫屬性
IsEnabled=true
和TextBlock
內(nèi)容變化時,啟動動畫效果
在創(chuàng)建關(guān)鍵幀設置跳動字符位置時剔除了空字符,是為了是動畫效果顯得連貫
public class DanceCharEffectBehavior : Behavior<TextBlock> { private TextEffect _textEffect; private string _textEffectName; private TranslateTransform _translateTransform = null; private string _translateTransformName; private Storyboard _storyboard; protected override void OnAttached() { base.OnAttached(); this.AssociatedObject.Loaded += AssociatedObject_Loaded; this.AssociatedObject.Unloaded += AssociatedObject_Unloaded; this.AssociatedObject.IsVisibleChanged += AssociatedObject_IsVisibleChanged; BindingOperations.SetBinding(this, DanceCharEffectBehavior.InternalTextProperty, new Binding("Text") { Source = this.AssociatedObject }); } protected override void OnDetaching() { base.OnDetaching(); this.AssociatedObject.Loaded -= AssociatedObject_Loaded; this.AssociatedObject.Unloaded -= AssociatedObject_Unloaded; this.AssociatedObject.IsVisibleChanged -= AssociatedObject_IsVisibleChanged; this.ClearValue(DanceCharEffectBehavior.InternalTextProperty); if (_storyboard != null) { _storyboard.Remove(this.AssociatedObject); _storyboard.Children.Clear(); } if (_textEffect != null) this.AssociatedObject.TextEffects.Remove(_textEffect); } private void AssociatedObject_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { if ((bool)e.NewValue == false) { if (_storyboard != null) _storyboard.Stop(this.AssociatedObject); } else { BeginEffect(this.AssociatedObject.Text); } } private void AssociatedObject_Loaded(object sender, RoutedEventArgs e) { if (_textEffect == null) { this.AssociatedObject.TextEffects.Add(_textEffect = new TextEffect() { PositionCount = 1, Transform = _translateTransform = new TranslateTransform(), }); NameScope.SetNameScope(this.AssociatedObject, new NameScope()); this.AssociatedObject.RegisterName(_textEffectName = "n" + Guid.NewGuid().ToString("N"), _textEffect); this.AssociatedObject.RegisterName(_translateTransformName = "n" + Guid.NewGuid().ToString("N"), _translateTransform); if (IsEnabled) BeginEffect(this.AssociatedObject.Text); } } private void AssociatedObject_Unloaded(object sender, RoutedEventArgs e) { StopEffect(); } 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 || _translateTransformName == null || IsEnabled == false) return; if (_storyboard == null) _storyboard = new Storyboard(); double duration = 0.5d; DoubleAnimation da = new DoubleAnimation(); Storyboard.SetTargetName(da, _translateTransformName); Storyboard.SetTargetProperty(da, new PropertyPath(TranslateTransform.YProperty)); da.From = 0d; da.To = 10d; da.Duration = TimeSpan.FromSeconds(duration / 2d); da.RepeatBehavior = RepeatBehavior.Forever; da.AutoReverse = true; char emptyChar = ' '; List<int> lsb = new List<int>(); for (int i = 0; i < textLength; ++i) { if (text[i] != emptyChar) { lsb.Add(i); } } Int32AnimationUsingKeyFrames frames = new Int32AnimationUsingKeyFrames(); Storyboard.SetTargetName(frames, _textEffectName); Storyboard.SetTargetProperty(frames, new PropertyPath(TextEffect.PositionStartProperty)); frames.Duration = TimeSpan.FromSeconds((lsb.Count) * duration); frames.RepeatBehavior = RepeatBehavior.Forever; frames.AutoReverse = true; int ii = 0; foreach (int index in lsb) { frames.KeyFrames.Add(new DiscreteInt32KeyFrame() { Value = index, KeyTime = TimeSpan.FromSeconds(ii * duration), }); ++ii; } _storyboard.Children.Add(da); _storyboard.Children.Add(frames); _storyboard.Begin(this.AssociatedObject, true); } private string InternalText { get { return (string)GetValue(InternalTextProperty); } set { SetValue(InternalTextProperty, value); } } private static readonly DependencyProperty InternalTextProperty = DependencyProperty.Register("InternalText", typeof(string), typeof(DanceCharEffectBehavior), new PropertyMetadata(OnInternalTextChanged)); private static void OnInternalTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var source = d as DanceCharEffectBehavior; 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(DanceCharEffectBehavior), new PropertyMetadata(true, (d, e) => { bool b = (bool)e.NewValue; var source = d as DanceCharEffectBehavior; source.SetEffect(source.InternalText); })); }
調(diào)用的時候只需要在TextBlock
添加Behavior即可,代碼如下
<TextBlock FontSize="20" Text="Hello"> <i:Interaction.Behaviors> <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" /> </i:Interaction.Behaviors> </TextBlock>
結(jié)尾
本例中還有許多可以完善的地方,比如字符跳動的幅度可以根據(jù)實際的FontSize來設置,或者增加依賴屬性來控制;動畫是否倒退播放,是否循環(huán)播放,以及動畫的速度都可以通過增加依賴屬性在調(diào)用時靈活設置。
到此這篇關(guān)于基于WPF實現(xiàn)跳動的字符效果的文章就介紹到這了,更多相關(guān)WPF字符跳動效果內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用C# 的webBrowser寫模擬器時的javascript腳本調(diào)用問題
這篇文章主要介紹了C# 的webBrowser寫模擬器時的javascript腳本調(diào)用問題,需要的朋友可以參考下2017-07-07C#使用this關(guān)鍵字實現(xiàn)串聯(lián)構(gòu)造函數(shù)調(diào)用方法
這篇文章主要介紹了C#使用this關(guān)鍵字實現(xiàn)串聯(lián)構(gòu)造函數(shù)調(diào)用方法,實例分析了使用this關(guān)鍵字串聯(lián)構(gòu)造函數(shù)的技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-01-01深入分析緩存依賴中cachedependency對象及周邊小講
本篇文章是對緩存依賴中cachedependency對象進行了詳細的分析介紹,需要的朋友參考下2013-06-06c#基于WinForm的Socket實現(xiàn)簡單的聊天室 IM
這篇文章主要介紹了c#基于WinForm的Socket實現(xiàn)簡單的聊天室 IM的步驟,幫助大家更好的理解和學習使用c#,感興趣的朋友可以了解下2021-05-05C#實現(xiàn)Word轉(zhuǎn)PDF的方法總結(jié)
這篇文章主要為大家詳細介紹了C#中實現(xiàn)Word轉(zhuǎn)PDF的常用方法,文中的示例代碼講解詳細,具有一定的學習價值,有需要的小伙伴可以參考下2023-10-10