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