1
1
using System ;
2
2
using System . Collections . Generic ;
3
+ using System . Diagnostics ;
3
4
using System . Linq ;
4
5
using System . Text ;
5
6
using System . Threading . Tasks ;
6
7
using System . Windows ;
7
8
using System . Windows . Controls ;
8
9
using System . Windows . Data ;
9
10
using System . Windows . Documents ;
11
+ using System . Windows . Documents . DocumentStructures ;
10
12
using System . Windows . Input ;
11
13
using System . Windows . Media ;
14
+ using System . Windows . Media . Animation ;
12
15
using System . Windows . Media . Imaging ;
13
16
using System . Windows . Navigation ;
14
17
using System . Windows . Shapes ;
15
18
16
19
namespace MaterialDesignThemes . Wpf
17
- {
20
+ {
21
+ [ TemplatePart ( Name = PartBubbleEllipse , Type = typeof ( Ellipse ) ) ]
18
22
public class Ripple : ContentControl
19
23
{
24
+ public const string PartBubbleEllipse = "PART_BubbleEllipse" ;
25
+
26
+ private BubbleStoryboardController _bubbleStoryboardController ;
27
+
20
28
static Ripple ( )
21
29
{
22
30
DefaultStyleKeyProperty . OverrideMetadata ( typeof ( Ripple ) , new FrameworkPropertyMetadata ( typeof ( Ripple ) ) ) ;
23
31
}
24
32
25
33
public Ripple ( )
26
- {
27
- MouseMove += OnMouseMove ;
34
+ {
35
+ MouseMove += OnMouseMove ;
28
36
}
29
37
30
38
protected override void OnPreviewMouseLeftButtonDown ( MouseButtonEventArgs e )
@@ -33,6 +41,8 @@ protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
33
41
MouseLeftButtonDownX = position . X ;
34
42
MouseLeftButtonDownY = position . Y ;
35
43
44
+ this . ReleaseMouseCapture ( ) ;
45
+
36
46
base . OnPreviewMouseLeftButtonDown ( e ) ;
37
47
}
38
48
@@ -43,70 +53,229 @@ private void OnMouseMove(object sender, MouseEventArgs mouseEventArgs)
43
53
MouseY = position . Y ;
44
54
}
45
55
56
+ public static readonly DependencyProperty IsActiveProperty = DependencyProperty . Register (
57
+ "IsActive" , typeof ( bool ) , typeof ( Ripple ) , new FrameworkPropertyMetadata ( false , IsActivePropertyChangedCallback ) ) ;
58
+
59
+ public bool IsActive
60
+ {
61
+ get { return ( bool ) GetValue ( IsActiveProperty ) ; }
62
+ set { SetValue ( IsActiveProperty , value ) ; }
63
+ }
64
+
65
+ public override void OnApplyTemplate ( )
66
+ {
67
+ base . OnApplyTemplate ( ) ;
68
+
69
+ _bubbleStoryboardController = new BubbleStoryboardController ( this ) ;
70
+ }
71
+
72
+ private void StartBubbleAnimation ( )
73
+ {
74
+ _bubbleStoryboardController . Add ( ) ;
75
+ }
76
+
77
+ private void StopBubbleAnimation ( )
78
+ {
79
+ _bubbleStoryboardController . Remove ( ) ;
80
+ }
81
+
82
+ private static void IsActivePropertyChangedCallback (
83
+ DependencyObject dependencyObject ,
84
+ DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs )
85
+ {
86
+ var box = dependencyObject as Ripple ;
87
+ if ( box == null ) return ;
88
+
89
+ bool isActive = ( bool ) dependencyPropertyChangedEventArgs . NewValue ;
90
+
91
+ if ( isActive )
92
+ {
93
+ box . StartBubbleAnimation ( ) ;
94
+ }
95
+ else
96
+ {
97
+ box . StopBubbleAnimation ( ) ;
98
+ }
99
+ }
100
+
46
101
public static readonly DependencyProperty FeedbackProperty = DependencyProperty . Register (
47
- "Feedback" , typeof ( Brush ) , typeof ( Ripple ) , new PropertyMetadata ( default ( Brush ) ) ) ;
102
+ "Feedback" , typeof ( Brush ) , typeof ( Ripple ) , new PropertyMetadata ( default ( Brush ) ) ) ;
48
103
49
104
public Brush Feedback
50
105
{
51
- get { return ( Brush ) GetValue ( FeedbackProperty ) ; }
106
+ get { return ( Brush ) GetValue ( FeedbackProperty ) ; }
52
107
set { SetValue ( FeedbackProperty , value ) ; }
53
108
}
54
109
55
110
private static readonly DependencyPropertyKey MouseXPropertyKey =
56
111
DependencyProperty . RegisterReadOnly (
57
- "MouseX" , typeof ( double ) , typeof ( Ripple ) ,
112
+ "MouseX" , typeof ( double ) , typeof ( Ripple ) ,
58
113
new PropertyMetadata ( default ( double ) ) ) ;
59
114
60
115
public static readonly DependencyProperty MouseXProperty =
61
116
MouseXPropertyKey . DependencyProperty ;
62
117
63
118
public double MouseX
64
119
{
65
- get { return ( double ) GetValue ( MouseXProperty ) ; }
120
+ get { return ( double ) GetValue ( MouseXProperty ) ; }
66
121
private set { SetValue ( MouseXPropertyKey , value ) ; }
67
122
}
68
123
69
124
private static readonly DependencyPropertyKey MouseYPropertyKey =
70
125
DependencyProperty . RegisterReadOnly (
71
- "MouseY" , typeof ( double ) , typeof ( Ripple ) ,
126
+ "MouseY" , typeof ( double ) , typeof ( Ripple ) ,
72
127
new PropertyMetadata ( default ( double ) ) ) ;
73
128
74
129
public static readonly DependencyProperty MouseYProperty =
75
130
MouseYPropertyKey . DependencyProperty ;
76
131
77
132
public double MouseY
78
133
{
79
- get { return ( double ) GetValue ( MouseYProperty ) ; }
134
+ get { return ( double ) GetValue ( MouseYProperty ) ; }
80
135
private set { SetValue ( MouseYPropertyKey , value ) ; }
81
136
}
82
137
83
138
private static readonly DependencyPropertyKey MouseLeftButtonDownXPropertyKey =
84
139
DependencyProperty . RegisterReadOnly (
85
- "MouseLeftButtonDownX" , typeof ( double ) , typeof ( Ripple ) ,
140
+ "MouseLeftButtonDownX" , typeof ( double ) , typeof ( Ripple ) ,
86
141
new PropertyMetadata ( default ( double ) ) ) ;
87
142
88
143
public static readonly DependencyProperty MouseLeftButtonDownXProperty =
89
144
MouseLeftButtonDownXPropertyKey . DependencyProperty ;
90
145
91
146
public double MouseLeftButtonDownX
92
147
{
93
- get { return ( double ) GetValue ( MouseLeftButtonDownXProperty ) ; }
148
+ get { return ( double ) GetValue ( MouseLeftButtonDownXProperty ) ; }
94
149
private set { SetValue ( MouseLeftButtonDownXPropertyKey , value ) ; }
95
150
}
96
151
97
152
private static readonly DependencyPropertyKey MouseLeftButtonDownYPropertyKey =
98
153
DependencyProperty . RegisterReadOnly (
99
- "MouseLeftButtonDownY" , typeof ( double ) , typeof ( Ripple ) ,
154
+ "MouseLeftButtonDownY" , typeof ( double ) , typeof ( Ripple ) ,
100
155
new PropertyMetadata ( default ( double ) ) ) ;
101
156
102
157
public static readonly DependencyProperty MouseLeftButtonDownYProperty =
103
158
MouseLeftButtonDownYPropertyKey . DependencyProperty ;
104
159
105
160
public double MouseLeftButtonDownY
106
161
{
107
- get { return ( double ) GetValue ( MouseLeftButtonDownYProperty ) ; }
162
+ get { return ( double ) GetValue ( MouseLeftButtonDownYProperty ) ; }
108
163
private set { SetValue ( MouseLeftButtonDownYPropertyKey , value ) ; }
109
164
}
110
165
166
+
167
+ private sealed class BubbleStoryboardController
168
+ {
169
+ private readonly Ripple _ripple ;
170
+ private readonly Ellipse _ellipse ;
171
+ private readonly TimeSpan _fadeOutDuration = TimeSpan . FromMilliseconds ( 200 ) ;
172
+ private readonly TimeSpan _bubbleDuration = TimeSpan . FromMilliseconds ( 400 ) ;
173
+
174
+ private volatile Storyboard _currentBubbleStoryboard ;
175
+ private volatile Storyboard _currentFadeOutStoryboard ;
176
+
177
+ public BubbleStoryboardController ( Ripple ripple )
178
+ {
179
+ _ripple = ripple ;
180
+ _ellipse = _ripple . Template . FindName ( PartBubbleEllipse , _ripple ) as Ellipse ;
181
+
182
+ if ( _ellipse == null ) throw new InvalidOperationException ( ) ;
183
+ }
184
+ public void Add ( )
185
+ {
186
+ _currentFadeOutStoryboard ? . Remove ( ) ;
187
+ _currentBubbleStoryboard ? . Remove ( ) ;
188
+
189
+ _currentBubbleStoryboard = CreateBubbleStoryboard ( ) ;
190
+ _currentBubbleStoryboard . Begin ( ) ;
191
+ }
192
+
193
+ public void Remove ( )
194
+ {
195
+ _currentFadeOutStoryboard ? . Remove ( ) ;
196
+
197
+ var fadeOutStoryboard = CreateFadeOutStoryboard ( ) ;
198
+ var bubbleStoryboardLocalCopy = _currentBubbleStoryboard ;
199
+
200
+ fadeOutStoryboard . Completed += delegate
201
+ {
202
+ fadeOutStoryboard . Remove ( ) ;
203
+ bubbleStoryboardLocalCopy ? . Remove ( ) ;
204
+ } ;
205
+
206
+ _currentFadeOutStoryboard = fadeOutStoryboard ;
207
+ _currentFadeOutStoryboard . Begin ( ) ;
208
+ }
209
+
210
+ private Storyboard CreateFadeOutStoryboard ( )
211
+ {
212
+ Storyboard resultStorybaord = new Storyboard ( ) ;
213
+
214
+ DoubleAnimation opacityAnimation = new DoubleAnimation
215
+ {
216
+ To = 0
217
+ } ;
218
+ SetupAnimation ( opacityAnimation , _fadeOutDuration , "(UIElement.Opacity)" ) ;
219
+
220
+ resultStorybaord . Children . Add ( opacityAnimation ) ;
221
+
222
+ return resultStorybaord ;
223
+ }
224
+
225
+ private Storyboard CreateBubbleStoryboard ( )
226
+ {
227
+ Storyboard resultStorybaord = new Storyboard ( ) ;
228
+
229
+ DoubleAnimation opacityAnimation = new DoubleAnimation
230
+ {
231
+ To = 0.26 ,
232
+ } ;
233
+ SetupAnimation ( opacityAnimation , _bubbleDuration , "(UIElement.Opacity)" ) ;
234
+
235
+ double h = Math . Max ( _ripple . MouseLeftButtonDownX , _ripple . ActualWidth - _ripple . MouseLeftButtonDownX ) ;
236
+ double w = Math . Max ( _ripple . MouseLeftButtonDownY , _ripple . ActualHeight - _ripple . MouseLeftButtonDownY ) ;
237
+ double diameter = 2 * Math . Sqrt ( h * h + w * w ) ;
238
+
239
+ DoubleAnimation widthAnimation = new DoubleAnimation
240
+ {
241
+ To = diameter
242
+ } ;
243
+ SetupAnimation ( widthAnimation , _bubbleDuration , "(FrameworkElement.Width)" ) ;
244
+
245
+ DoubleAnimation heightAnimation = new DoubleAnimation
246
+ {
247
+ To = diameter
248
+ } ;
249
+ SetupAnimation ( heightAnimation , _bubbleDuration , "(FrameworkElement.Height)" ) ;
250
+
251
+ DoubleAnimation translateXAnimation = new DoubleAnimation
252
+ {
253
+ To = - diameter / 2
254
+ } ;
255
+ SetupAnimation ( translateXAnimation , _bubbleDuration , "(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" ) ;
256
+
257
+ DoubleAnimation translateYAnimation = new DoubleAnimation
258
+ {
259
+ To = - diameter / 2
260
+ } ;
261
+ SetupAnimation ( translateYAnimation , _bubbleDuration , "(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" ) ;
262
+
263
+ resultStorybaord . Children . Add ( opacityAnimation ) ;
264
+ resultStorybaord . Children . Add ( widthAnimation ) ;
265
+ resultStorybaord . Children . Add ( heightAnimation ) ;
266
+ resultStorybaord . Children . Add ( translateXAnimation ) ;
267
+ resultStorybaord . Children . Add ( translateYAnimation ) ;
268
+
269
+ return resultStorybaord ;
270
+ }
271
+
272
+ private void SetupAnimation ( AnimationTimeline animation , TimeSpan duration , string propertyPath )
273
+ {
274
+ animation . AccelerationRatio = 0.25 ;
275
+ animation . Duration = new Duration ( duration ) ;
276
+ Storyboard . SetTarget ( animation , _ellipse ) ;
277
+ Storyboard . SetTargetProperty ( animation , new PropertyPath ( propertyPath ) ) ;
278
+ }
279
+ }
111
280
}
112
281
}
0 commit comments