UNPKG

16.2 kBJavaScriptView Raw
1var __extends = (this && this.__extends) || function (d, b) {
2 for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
3 function __() { this.constructor = d; }
4 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
5};
6var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
7 var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
8 if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
9 else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
10 return c > 3 && r && Object.defineProperty(target, key, r), r;
11};
12var __metadata = (this && this.__metadata) || function (k, v) {
13 if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
14};
15import { NgModule, Component, HostBinding, ChangeDetectorRef, ChangeDetectionStrategy, Input } from '@angular/core';
16// TODO(josephperrott): Benchpress tests.
17/** A single degree in radians. */
18var DEGREE_IN_RADIANS = Math.PI / 180;
19/** Duration of the indeterminate animation. */
20var DURATION_INDETERMINATE = 667;
21/** Duration of the indeterminate animation. */
22var DURATION_DETERMINATE = 225;
23/** Start animation value of the indeterminate animation */
24var startIndeterminate = 3;
25/** End animation value of the indeterminate animation */
26var endIndeterminate = 80;
27/**
28 * <md-progress-circle> component.
29 */
30export var MdProgressCircle = (function () {
31 function MdProgressCircle(_changeDetectorRef) {
32 this._changeDetectorRef = _changeDetectorRef;
33 /** The id of the last requested animation. */
34 this._lastAnimationId = 0;
35 this._mode = 'determinate';
36 }
37 Object.defineProperty(MdProgressCircle.prototype, "_ariaValueMin", {
38 /**
39 * Values for aria max and min are only defined as numbers when in a determinate mode. We do this
40 * because voiceover does not report the progress indicator as indeterminate if the aria min
41 * and/or max value are number values.
42 */
43 get: function () {
44 return this.mode == 'determinate' ? 0 : null;
45 },
46 enumerable: true,
47 configurable: true
48 });
49 Object.defineProperty(MdProgressCircle.prototype, "_ariaValueMax", {
50 get: function () {
51 return this.mode == 'determinate' ? 100 : null;
52 },
53 enumerable: true,
54 configurable: true
55 });
56 Object.defineProperty(MdProgressCircle.prototype, "interdeterminateInterval", {
57 /** TODO: internal */
58 get: function () {
59 return this._interdeterminateInterval;
60 },
61 /** TODO: internal */
62 set: function (interval) {
63 clearInterval(this._interdeterminateInterval);
64 this._interdeterminateInterval = interval;
65 },
66 enumerable: true,
67 configurable: true
68 });
69 Object.defineProperty(MdProgressCircle.prototype, "currentPath", {
70 /** TODO: internal */
71 get: function () {
72 return this._currentPath;
73 },
74 set: function (path) {
75 this._currentPath = path;
76 // Mark for check as our ChangeDetectionStrategy is OnPush, when changes come from within the
77 // component, change detection must be called for.
78 this._changeDetectorRef.markForCheck();
79 },
80 enumerable: true,
81 configurable: true
82 });
83 /** Clean up any animations that were running. */
84 MdProgressCircle.prototype.ngOnDestroy = function () {
85 this._cleanupIndeterminateAnimation();
86 };
87 Object.defineProperty(MdProgressCircle.prototype, "value", {
88 get: function () {
89 if (this.mode == 'determinate') {
90 return this._value;
91 }
92 },
93 set: function (v) {
94 if (v && this.mode == 'determinate') {
95 var newValue = clamp(v);
96 this._animateCircle((this.value || 0), newValue, linearEase, DURATION_DETERMINATE, 0);
97 this._value = newValue;
98 }
99 },
100 enumerable: true,
101 configurable: true
102 });
103 Object.defineProperty(MdProgressCircle.prototype, "mode", {
104 /**
105 * Mode of the progress circle
106 *
107 * Input must be one of the values from ProgressMode, defaults to 'determinate'.
108 * mode is bound to the host as the attribute host.
109 */
110 get: function () {
111 return this._mode;
112 },
113 set: function (m) {
114 if (m == 'indeterminate') {
115 this._startIndeterminateAnimation();
116 }
117 else {
118 this._cleanupIndeterminateAnimation();
119 }
120 this._mode = m;
121 },
122 enumerable: true,
123 configurable: true
124 });
125 /**
126 * Animates the circle from one percentage value to another.
127 *
128 * @param animateFrom The percentage of the circle filled starting the animation.
129 * @param animateTo The percentage of the circle filled ending the animation.
130 * @param ease The easing function to manage the pace of change in the animation.
131 * @param duration The length of time to show the animation, in milliseconds.
132 * @param rotation The starting angle of the circle fill, with 0° represented at the top center
133 * of the circle.
134 */
135 MdProgressCircle.prototype._animateCircle = function (animateFrom, animateTo, ease, duration, rotation) {
136 var _this = this;
137 var id = ++this._lastAnimationId;
138 var startTime = Date.now();
139 var changeInValue = animateTo - animateFrom;
140 // No need to animate it if the values are the same
141 if (animateTo === animateFrom) {
142 this.currentPath = getSvgArc(animateTo, rotation);
143 }
144 else {
145 var animation_1 = function () {
146 var elapsedTime = Math.max(0, Math.min(Date.now() - startTime, duration));
147 _this.currentPath = getSvgArc(ease(elapsedTime, animateFrom, changeInValue, duration), rotation);
148 // Prevent overlapping animations by checking if a new animation has been called for and
149 // if the animation has lasted long than the animation duration.
150 if (id === _this._lastAnimationId && elapsedTime < duration) {
151 requestAnimationFrame(animation_1);
152 }
153 };
154 requestAnimationFrame(animation_1);
155 }
156 };
157 /**
158 * Starts the indeterminate animation interval, if it is not already running.
159 */
160 MdProgressCircle.prototype._startIndeterminateAnimation = function () {
161 var _this = this;
162 var rotationStartPoint = 0;
163 var start = startIndeterminate;
164 var end = endIndeterminate;
165 var duration = DURATION_INDETERMINATE;
166 var animate = function () {
167 _this._animateCircle(start, end, materialEase, duration, rotationStartPoint);
168 // Prevent rotation from reaching Number.MAX_SAFE_INTEGER.
169 rotationStartPoint = (rotationStartPoint + end) % 100;
170 var temp = start;
171 start = -end;
172 end = -temp;
173 };
174 if (!this.interdeterminateInterval) {
175 this.interdeterminateInterval = setInterval(animate, duration + 50, 0, false);
176 animate();
177 }
178 };
179 /**
180 * Removes interval, ending the animation.
181 */
182 MdProgressCircle.prototype._cleanupIndeterminateAnimation = function () {
183 this.interdeterminateInterval = null;
184 };
185 __decorate([
186 Input(),
187 HostBinding('attr.aria-valuenow'),
188 __metadata('design:type', Object)
189 ], MdProgressCircle.prototype, "value", null);
190 __decorate([
191 HostBinding('attr.mode'),
192 Input(),
193 __metadata('design:type', Object)
194 ], MdProgressCircle.prototype, "mode", null);
195 MdProgressCircle = __decorate([
196 Component({selector: 'md-progress-circle',
197 host: {
198 'role': 'progressbar',
199 '[attr.aria-valuemin]': '_ariaValueMin',
200 '[attr.aria-valuemax]': '_ariaValueMax',
201 },
202 template: "<!-- preserveAspectRatio of xMidYMid meet as the center of the viewport is the circle's center. The center of the circle with remain at the center of the md-progress-circle element containing the SVG. --> <svg viewBox=\"0 0 100 100\" preserveAspectRatio=\"xMidYMid meet\"> <path [attr.d]=\"currentPath\"></path> </svg> ",
203 styles: ["/* Animation Durations */ /** Component sizing */ :host { display: block; /** Height and width are provided for md-progress-circle to act as a default. The height and width are expected to be overwritten by application css. */ height: 100px; width: 100px; /** SVG's viewBox is defined as 0 0 100 100, this means that all SVG children will placed based on a 100px by 100px box. Additionally all SVG sizes and locations are in reference to this viewBox. */ } :host svg { height: 100%; width: 100%; transform-origin: center; } :host path { fill: transparent; stroke: #00897b; /** Stroke width of 10px defines stroke as 10% of the viewBox */ stroke-width: 10px; } :host[color='accent'] path { stroke: #8e24aa; } :host[color='warn'] path { stroke: #e53935; } :host[mode='indeterminate'] { animation-duration: 5250ms, 2887.5ms; animation-name: md-progress-circle-sporadic-rotate, md-progress-circle-linear-rotate; animation-timing-function: cubic-bezier(0.35, 0, 0.25, 1), linear; animation-iteration-count: infinite; transition: none; } /** Animations for indeterminate mode */ @keyframes md-progress-circle-linear-rotate { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @keyframes md-progress-circle-sporadic-rotate { 12.5% { transform: rotate(135deg); } 25% { transform: rotate(270deg); } 37.5% { transform: rotate(405deg); } 50% { transform: rotate(540deg); } 62.5% { transform: rotate(675deg); } 75% { transform: rotate(810deg); } 87.5% { transform: rotate(945deg); } 100% { transform: rotate(1080deg); } } /*# sourceMappingURL=progress-circle.css.map */ "],
204 changeDetection: ChangeDetectionStrategy.OnPush,
205 }),
206 __metadata('design:paramtypes', [ChangeDetectorRef])
207 ], MdProgressCircle);
208 return MdProgressCircle;
209}());
210/**
211 * <md-spinner> component.
212 *
213 * This is a component definition to be used as a convenience reference to create an
214 * indeterminate <md-progress-circle> instance.
215 */
216export var MdSpinner = (function (_super) {
217 __extends(MdSpinner, _super);
218 function MdSpinner(changeDetectorRef) {
219 _super.call(this, changeDetectorRef);
220 this.mode = 'indeterminate';
221 }
222 MdSpinner = __decorate([
223 Component({selector: 'md-spinner',
224 host: {
225 'role': 'progressbar',
226 'mode': 'indeterminate',
227 },
228 template: "<!-- preserveAspectRatio of xMidYMid meet as the center of the viewport is the circle's center. The center of the circle with remain at the center of the md-progress-circle element containing the SVG. --> <svg viewBox=\"0 0 100 100\" preserveAspectRatio=\"xMidYMid meet\"> <path [attr.d]=\"currentPath\"></path> </svg> ",
229 styles: ["/* Animation Durations */ /** Component sizing */ :host { display: block; /** Height and width are provided for md-progress-circle to act as a default. The height and width are expected to be overwritten by application css. */ height: 100px; width: 100px; /** SVG's viewBox is defined as 0 0 100 100, this means that all SVG children will placed based on a 100px by 100px box. Additionally all SVG sizes and locations are in reference to this viewBox. */ } :host svg { height: 100%; width: 100%; transform-origin: center; } :host path { fill: transparent; stroke: #00897b; /** Stroke width of 10px defines stroke as 10% of the viewBox */ stroke-width: 10px; } :host[color='accent'] path { stroke: #8e24aa; } :host[color='warn'] path { stroke: #e53935; } :host[mode='indeterminate'] { animation-duration: 5250ms, 2887.5ms; animation-name: md-progress-circle-sporadic-rotate, md-progress-circle-linear-rotate; animation-timing-function: cubic-bezier(0.35, 0, 0.25, 1), linear; animation-iteration-count: infinite; transition: none; } /** Animations for indeterminate mode */ @keyframes md-progress-circle-linear-rotate { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @keyframes md-progress-circle-sporadic-rotate { 12.5% { transform: rotate(135deg); } 25% { transform: rotate(270deg); } 37.5% { transform: rotate(405deg); } 50% { transform: rotate(540deg); } 62.5% { transform: rotate(675deg); } 75% { transform: rotate(810deg); } 87.5% { transform: rotate(945deg); } 100% { transform: rotate(1080deg); } } /*# sourceMappingURL=progress-circle.css.map */ "],
230 }),
231 __metadata('design:paramtypes', [ChangeDetectorRef])
232 ], MdSpinner);
233 return MdSpinner;
234}(MdProgressCircle));
235/**
236 * Module functions.
237 */
238/** Clamps a value to be between 0 and 100. */
239function clamp(v) {
240 return Math.max(0, Math.min(100, v));
241}
242/**
243 * Converts Polar coordinates to Cartesian.
244 */
245function polarToCartesian(radius, pathRadius, angleInDegrees) {
246 var angleInRadians = (angleInDegrees - 90) * DEGREE_IN_RADIANS;
247 return (radius + (pathRadius * Math.cos(angleInRadians))) +
248 ',' + (radius + (pathRadius * Math.sin(angleInRadians)));
249}
250/**
251 * Easing function for linear animation.
252 */
253function linearEase(currentTime, startValue, changeInValue, duration) {
254 return changeInValue * currentTime / duration + startValue;
255}
256/**
257 * Easing function to match material design indeterminate animation.
258 */
259function materialEase(currentTime, startValue, changeInValue, duration) {
260 var time = currentTime / duration;
261 var timeCubed = Math.pow(time, 3);
262 var timeQuad = Math.pow(time, 4);
263 var timeQuint = Math.pow(time, 5);
264 return startValue + changeInValue * ((6 * timeQuint) + (-15 * timeQuad) + (10 * timeCubed));
265}
266/**
267 * Determines the path value to define the arc. Converting percentage values to to polar
268 * coordinates on the circle, and then to cartesian coordinates in the viewport.
269 *
270 * @param currentValue The current percentage value of the progress circle, the percentage of the
271 * circle to fill.
272 * @param rotation The starting point of the circle with 0 being the 0 degree point.
273 * @return A string for an SVG path representing a circle filled from the starting point to the
274 * percentage value provided.
275 */
276function getSvgArc(currentValue, rotation) {
277 // The angle can't be exactly 360, because the arc becomes hidden.
278 var maximumAngle = 359.99 / 100;
279 var startPoint = rotation || 0;
280 var radius = 50;
281 var pathRadius = 40;
282 var startAngle = startPoint * maximumAngle;
283 var endAngle = currentValue * maximumAngle;
284 var start = polarToCartesian(radius, pathRadius, startAngle);
285 var end = polarToCartesian(radius, pathRadius, endAngle + startAngle);
286 var arcSweep = endAngle < 0 ? 0 : 1;
287 var largeArcFlag;
288 if (endAngle < 0) {
289 largeArcFlag = endAngle >= -180 ? 0 : 1;
290 }
291 else {
292 largeArcFlag = endAngle <= 180 ? 0 : 1;
293 }
294 return "M" + start + "A" + pathRadius + "," + pathRadius + " 0 " + largeArcFlag + "," + arcSweep + " " + end;
295}
296export var MdProgressCircleModule = (function () {
297 function MdProgressCircleModule() {
298 }
299 MdProgressCircleModule.forRoot = function () {
300 return {
301 ngModule: MdProgressCircleModule,
302 providers: []
303 };
304 };
305 MdProgressCircleModule = __decorate([
306 NgModule({
307 exports: [MdProgressCircle, MdSpinner],
308 declarations: [MdProgressCircle, MdSpinner],
309 }),
310 __metadata('design:paramtypes', [])
311 ], MdProgressCircleModule);
312 return MdProgressCircleModule;
313}());
314
315//# sourceMappingURL=progress-circle.js.map