7
7
*/
8
8
9
9
import {
10
+ AfterViewInit ,
10
11
Component ,
11
12
ChangeDetectionStrategy ,
12
13
Input ,
13
14
ElementRef ,
14
15
Renderer2 ,
15
- Directive ,
16
- ViewChild ,
17
16
SimpleChanges ,
18
17
OnChanges ,
19
18
ViewEncapsulation ,
19
+ Optional ,
20
+ Inject ,
20
21
} from '@angular/core' ;
21
22
import { CanColor , mixinColor } from '@angular/material/core' ;
22
23
import { Platform } from '@angular/cdk/platform' ;
24
+ import { DOCUMENT } from '@angular/common' ;
23
25
24
26
/** Possible mode for a progress spinner. */
25
27
export type ProgressSpinnerMode = 'determinate' | 'indeterminate' ;
@@ -31,6 +33,38 @@ export class MdProgressSpinnerBase {
31
33
}
32
34
export const _MdProgressSpinnerMixinBase = mixinColor ( MdProgressSpinnerBase , 'primary' ) ;
33
35
36
+ /* tslint:disable:max-line-length */
37
+ const INDETERMINATE_ANIMATION_TEMPLATE = `
38
+ .mat-progress-spinner-DIAMETER.mat-progress-spinner-indeterminate-animation[mode="indeterminate"] circle {
39
+ animation: mat-progress-spinner-stroke-rotate-DIAMETER 4s
40
+ cubic-bezier(0.35, 0, 0.25, 1) infinite;
41
+ transition-property: stroke;
42
+ }
43
+
44
+ @keyframes mat-progress-spinner-stroke-rotate-DIAMETER {
45
+ 0% { stroke-dashoffset: START_VALUE; transform: rotate(0); }
46
+ 12.5% { stroke-dashoffset: END_VALUE; transform: rotate(0); }
47
+ 12.51% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(72.5deg); }
48
+ 25% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(72.5deg); }
49
+
50
+ 25.1% { stroke-dashoffset: START_VALUE; transform: rotate(270deg); }
51
+ 37.5% { stroke-dashoffset: END_VALUE; transform: rotate(270deg); }
52
+ 37.51% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(161.5deg); }
53
+ 50% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(161.5deg); }
54
+
55
+ 50.01% { stroke-dashoffset: START_VALUE; transform: rotate(180deg); }
56
+ 62.5% { stroke-dashoffset: END_VALUE; transform: rotate(180deg); }
57
+ 62.51% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(251.5deg); }
58
+ 75% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(251.5deg); }
59
+
60
+ 75.01% { stroke-dashoffset: START_VALUE; transform: rotate(90deg); }
61
+ 87.5% { stroke-dashoffset: END_VALUE; transform: rotate(90deg); }
62
+ 87.51% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(341.5deg); }
63
+ 100% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(341.5deg); }
64
+ }
65
+ ` ;
66
+ /* tslint:enable:max-line-length */
67
+
34
68
/**
35
69
* <md-progress-spinner> component.
36
70
*/
@@ -54,13 +88,30 @@ export const _MdProgressSpinnerMixinBase = mixinColor(MdProgressSpinnerBase, 'pr
54
88
encapsulation : ViewEncapsulation . None ,
55
89
preserveWhitespaces : false ,
56
90
} )
57
- export class MdProgressSpinner extends _MdProgressSpinnerMixinBase implements CanColor , OnChanges {
91
+ export class MdProgressSpinner extends _MdProgressSpinnerMixinBase implements CanColor ,
92
+ OnChanges , AfterViewInit {
93
+
58
94
private _value : number ;
59
95
private readonly _baseSize = 100 ;
60
96
private readonly _baseStrokeWidth = 10 ;
97
+ private _fallbackAnimation = false ;
61
98
99
+ /** The width and height of the host element. Will grow with stroke width. **/
62
100
_elementSize = this . _baseSize ;
63
- _circleRadius = 45 ;
101
+
102
+ /** Tracks diameters of existing instances to de-dupe generated styles (default d = 100) */
103
+ static diameters = new Set < number > ( [ 100 ] ) ;
104
+
105
+ /** The diameter of the progress spinner (will set width and height of svg). */
106
+ @Input ( )
107
+ get diameter ( ) : number {
108
+ return this . _diameter ;
109
+ }
110
+
111
+ set diameter ( size : number ) {
112
+ this . _setDiameterAndInitStyles ( size ) ;
113
+ }
114
+ _diameter = this . _baseSize ;
64
115
65
116
/** Stroke width of the progress spinner. */
66
117
@Input ( ) strokeWidth : number = 10 ;
@@ -79,31 +130,89 @@ export class MdProgressSpinner extends _MdProgressSpinnerMixinBase implements Ca
79
130
}
80
131
}
81
132
82
- constructor ( renderer : Renderer2 , elementRef : ElementRef , platform : Platform ) {
83
- super ( renderer , elementRef ) ;
133
+ constructor ( public _renderer : Renderer2 , public _elementRef : ElementRef ,
134
+ platform : Platform , @Optional ( ) @Inject ( DOCUMENT ) private _document : any ) {
135
+ super ( _renderer , _elementRef ) ;
136
+
137
+ this . _fallbackAnimation = platform . EDGE || platform . TRIDENT ;
84
138
85
- // On IE and Edge we can't animate the `stroke-dashoffset`
139
+ // On IE and Edge, we can't animate the `stroke-dashoffset`
86
140
// reliably so we fall back to a non-spec animation.
87
- const animationClass = ( platform . EDGE || platform . TRIDENT ) ?
141
+ const animationClass = this . _fallbackAnimation ?
88
142
'mat-progress-spinner-indeterminate-fallback-animation' :
89
143
'mat-progress-spinner-indeterminate-animation' ;
90
144
91
- renderer . addClass ( elementRef . nativeElement , animationClass ) ;
145
+ _renderer . addClass ( _elementRef . nativeElement , animationClass ) ;
146
+ }
147
+
148
+ ngAfterViewInit ( ) {
149
+ this . _setDiameterClass ( this . diameter ) ;
92
150
}
93
151
94
152
ngOnChanges ( changes : SimpleChanges ) {
95
- if ( changes . strokeWidth ) {
96
- this . _elementSize = this . _baseSize + Math . max ( this . strokeWidth - this . _baseStrokeWidth , 0 ) ;
153
+ if ( changes . strokeWidth || changes . diameter ) {
154
+ this . _elementSize =
155
+ this . _diameter + Math . max ( this . strokeWidth - this . _baseStrokeWidth , 0 ) ;
97
156
}
98
157
}
99
158
100
- _getStrokeDashOffset ( ) {
159
+ /** The radius of the spinner, adjusted for stroke width. */
160
+ get _circleRadius ( ) {
161
+ return ( this . diameter - this . _baseStrokeWidth ) / 2 ;
162
+ }
163
+
164
+ /** The view box of the spinner's svg element. */
165
+ get _viewBox ( ) {
166
+ return `0 0 ${ this . _elementSize } ${ this . _elementSize } ` ;
167
+ }
168
+
169
+ /** The stroke circumference of the svg circle. */
170
+ get _strokeCircumference ( ) : number {
171
+ return 2 * Math . PI * this . _circleRadius ;
172
+ }
173
+
174
+ /** The dash offset of the svg circle. */
175
+ get _strokeDashOffset ( ) {
101
176
if ( this . mode === 'determinate' ) {
102
- return 2 * Math . PI * this . _circleRadius * ( 100 - this . _value ) / 100 ;
177
+ return this . _strokeCircumference * ( 100 - this . _value ) / 100 ;
103
178
}
104
179
105
180
return null ;
106
181
}
182
+
183
+ /** Sets the diameter and adds diameter-specific styles if necessary. */
184
+ private _setDiameterAndInitStyles ( size : number ) : void {
185
+ this . _setDiameterClass ( size ) ;
186
+ this . _diameter = size ;
187
+ if ( ! MdProgressSpinner . diameters . has ( this . diameter ) && ! this . _fallbackAnimation ) {
188
+ this . _attachStyleNode ( ) ;
189
+ }
190
+ }
191
+
192
+ /** Adds a diameter-specific class and removes any existing diameter classes. */
193
+ private _setDiameterClass ( size : number ) : void {
194
+ this . _renderer . removeClass (
195
+ this . _elementRef . nativeElement , `mat-progress-spinner-${ this . diameter } ` ) ;
196
+ this . _renderer . addClass (
197
+ this . _elementRef . nativeElement , `mat-progress-spinner-${ size } ` ) ;
198
+ }
199
+
200
+ /** Dynamically generates a style tag containing the correct animation for this diameter. */
201
+ private _attachStyleNode ( ) : void {
202
+ const styleTag = this . _renderer . createElement ( 'style' ) ;
203
+ styleTag . textContent = this . _getAnimationText ( ) ;
204
+ this . _renderer . appendChild ( this . _document . head , styleTag ) ;
205
+ MdProgressSpinner . diameters . add ( this . diameter ) ;
206
+ }
207
+
208
+ /** Generates animation styles adjusted for the spinner's diameter. */
209
+ private _getAnimationText ( ) : string {
210
+ return INDETERMINATE_ANIMATION_TEMPLATE
211
+ // Animation should begin at 5% and end at 80%
212
+ . replace ( / S T A R T _ V A L U E / g, `${ 0.95 * this . _strokeCircumference } ` )
213
+ . replace ( / E N D _ V A L U E / g, `${ 0.2 * this . _strokeCircumference } ` )
214
+ . replace ( / D I A M E T E R / g, `${ this . diameter } ` ) ;
215
+ }
107
216
}
108
217
109
218
@@ -131,8 +240,9 @@ export class MdProgressSpinner extends _MdProgressSpinnerMixinBase implements Ca
131
240
preserveWhitespaces : false ,
132
241
} )
133
242
export class MdSpinner extends MdProgressSpinner {
134
- constructor ( renderer : Renderer2 , elementRef : ElementRef , platform : Platform ) {
135
- super ( renderer , elementRef , platform ) ;
243
+ constructor ( renderer : Renderer2 , elementRef : ElementRef , platform : Platform ,
244
+ @Optional ( ) @Inject ( DOCUMENT ) document : any ) {
245
+ super ( renderer , elementRef , platform , document ) ;
136
246
this . mode = 'indeterminate' ;
137
247
}
138
248
}
0 commit comments