@angular2-material/progress-circle
Version:
Angular 2 Material progress-circle
326 lines (322 loc) • 16.7 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) :
typeof define === 'function' && define.amd ? define(['exports', '@angular/core'], factory) :
(factory((global.md = global.md || {}, global.md.progressCircle = global.md.progressCircle || {}),global.ng.core));
}(this, (function (exports,_angular_core) { 'use strict';
var __extends = (window && window.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var __decorate = (window && window.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (window && window.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
// TODO(josephperrott): Benchpress tests.
/** A single degree in radians. */
var DEGREE_IN_RADIANS = Math.PI / 180;
/** Duration of the indeterminate animation. */
var DURATION_INDETERMINATE = 667;
/** Duration of the indeterminate animation. */
var DURATION_DETERMINATE = 225;
/** Start animation value of the indeterminate animation */
var startIndeterminate = 3;
/** End animation value of the indeterminate animation */
var endIndeterminate = 80;
/**
* <md-progress-circle> component.
*/
var MdProgressCircle = (function () {
function MdProgressCircle(_changeDetectorRef) {
this._changeDetectorRef = _changeDetectorRef;
/** The id of the last requested animation. */
this._lastAnimationId = 0;
this._mode = 'determinate';
}
Object.defineProperty(MdProgressCircle.prototype, "_ariaValueMin", {
/**
* Values for aria max and min are only defined as numbers when in a determinate mode. We do this
* because voiceover does not report the progress indicator as indeterminate if the aria min
* and/or max value are number values.
*/
get: function () {
return this.mode == 'determinate' ? 0 : null;
},
enumerable: true,
configurable: true
});
Object.defineProperty(MdProgressCircle.prototype, "_ariaValueMax", {
get: function () {
return this.mode == 'determinate' ? 100 : null;
},
enumerable: true,
configurable: true
});
Object.defineProperty(MdProgressCircle.prototype, "interdeterminateInterval", {
/** TODO: internal */
get: function () {
return this._interdeterminateInterval;
},
/** TODO: internal */
set: function (interval) {
clearInterval(this._interdeterminateInterval);
this._interdeterminateInterval = interval;
},
enumerable: true,
configurable: true
});
Object.defineProperty(MdProgressCircle.prototype, "currentPath", {
/** TODO: internal */
get: function () {
return this._currentPath;
},
set: function (path) {
this._currentPath = path;
// Mark for check as our ChangeDetectionStrategy is OnPush, when changes come from within the
// component, change detection must be called for.
this._changeDetectorRef.markForCheck();
},
enumerable: true,
configurable: true
});
/** Clean up any animations that were running. */
MdProgressCircle.prototype.ngOnDestroy = function () {
this._cleanupIndeterminateAnimation();
};
Object.defineProperty(MdProgressCircle.prototype, "value", {
get: function () {
if (this.mode == 'determinate') {
return this._value;
}
},
set: function (v) {
if (v && this.mode == 'determinate') {
var newValue = clamp(v);
this._animateCircle((this.value || 0), newValue, linearEase, DURATION_DETERMINATE, 0);
this._value = newValue;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(MdProgressCircle.prototype, "mode", {
/**
* Mode of the progress circle
*
* Input must be one of the values from ProgressMode, defaults to 'determinate'.
* mode is bound to the host as the attribute host.
*/
get: function () {
return this._mode;
},
set: function (m) {
if (m == 'indeterminate') {
this._startIndeterminateAnimation();
}
else {
this._cleanupIndeterminateAnimation();
}
this._mode = m;
},
enumerable: true,
configurable: true
});
/**
* Animates the circle from one percentage value to another.
*
* @param animateFrom The percentage of the circle filled starting the animation.
* @param animateTo The percentage of the circle filled ending the animation.
* @param ease The easing function to manage the pace of change in the animation.
* @param duration The length of time to show the animation, in milliseconds.
* @param rotation The starting angle of the circle fill, with 0° represented at the top center
* of the circle.
*/
MdProgressCircle.prototype._animateCircle = function (animateFrom, animateTo, ease, duration, rotation) {
var _this = this;
var id = ++this._lastAnimationId;
var startTime = Date.now();
var changeInValue = animateTo - animateFrom;
// No need to animate it if the values are the same
if (animateTo === animateFrom) {
this.currentPath = getSvgArc(animateTo, rotation);
}
else {
var animation_1 = function () {
var elapsedTime = Math.max(0, Math.min(Date.now() - startTime, duration));
_this.currentPath = getSvgArc(ease(elapsedTime, animateFrom, changeInValue, duration), rotation);
// Prevent overlapping animations by checking if a new animation has been called for and
// if the animation has lasted long than the animation duration.
if (id === _this._lastAnimationId && elapsedTime < duration) {
requestAnimationFrame(animation_1);
}
};
requestAnimationFrame(animation_1);
}
};
/**
* Starts the indeterminate animation interval, if it is not already running.
*/
MdProgressCircle.prototype._startIndeterminateAnimation = function () {
var _this = this;
var rotationStartPoint = 0;
var start = startIndeterminate;
var end = endIndeterminate;
var duration = DURATION_INDETERMINATE;
var animate = function () {
_this._animateCircle(start, end, materialEase, duration, rotationStartPoint);
// Prevent rotation from reaching Number.MAX_SAFE_INTEGER.
rotationStartPoint = (rotationStartPoint + end) % 100;
var temp = start;
start = -end;
end = -temp;
};
if (!this.interdeterminateInterval) {
this.interdeterminateInterval = setInterval(animate, duration + 50, 0, false);
animate();
}
};
/**
* Removes interval, ending the animation.
*/
MdProgressCircle.prototype._cleanupIndeterminateAnimation = function () {
this.interdeterminateInterval = null;
};
__decorate([
_angular_core.Input(),
_angular_core.HostBinding('attr.aria-valuenow'),
__metadata('design:type', Object)
], MdProgressCircle.prototype, "value", null);
__decorate([
_angular_core.HostBinding('attr.mode'),
_angular_core.Input(),
__metadata('design:type', Object)
], MdProgressCircle.prototype, "mode", null);
MdProgressCircle = __decorate([
_angular_core.Component({selector: 'md-progress-circle',
host: {
'role': 'progressbar',
'[attr.aria-valuemin]': '_ariaValueMin',
'[attr.aria-valuemax]': '_ariaValueMax',
},
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> ",
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 */ "],
changeDetection: _angular_core.ChangeDetectionStrategy.OnPush,
}),
__metadata('design:paramtypes', [_angular_core.ChangeDetectorRef])
], MdProgressCircle);
return MdProgressCircle;
}());
/**
* <md-spinner> component.
*
* This is a component definition to be used as a convenience reference to create an
* indeterminate <md-progress-circle> instance.
*/
var MdSpinner = (function (_super) {
__extends(MdSpinner, _super);
function MdSpinner(changeDetectorRef) {
_super.call(this, changeDetectorRef);
this.mode = 'indeterminate';
}
MdSpinner = __decorate([
_angular_core.Component({selector: 'md-spinner',
host: {
'role': 'progressbar',
'mode': 'indeterminate',
},
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> ",
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 */ "],
}),
__metadata('design:paramtypes', [_angular_core.ChangeDetectorRef])
], MdSpinner);
return MdSpinner;
}(MdProgressCircle));
/**
* Module functions.
*/
/** Clamps a value to be between 0 and 100. */
function clamp(v) {
return Math.max(0, Math.min(100, v));
}
/**
* Converts Polar coordinates to Cartesian.
*/
function polarToCartesian(radius, pathRadius, angleInDegrees) {
var angleInRadians = (angleInDegrees - 90) * DEGREE_IN_RADIANS;
return (radius + (pathRadius * Math.cos(angleInRadians))) +
',' + (radius + (pathRadius * Math.sin(angleInRadians)));
}
/**
* Easing function for linear animation.
*/
function linearEase(currentTime, startValue, changeInValue, duration) {
return changeInValue * currentTime / duration + startValue;
}
/**
* Easing function to match material design indeterminate animation.
*/
function materialEase(currentTime, startValue, changeInValue, duration) {
var time = currentTime / duration;
var timeCubed = Math.pow(time, 3);
var timeQuad = Math.pow(time, 4);
var timeQuint = Math.pow(time, 5);
return startValue + changeInValue * ((6 * timeQuint) + (-15 * timeQuad) + (10 * timeCubed));
}
/**
* Determines the path value to define the arc. Converting percentage values to to polar
* coordinates on the circle, and then to cartesian coordinates in the viewport.
*
* @param currentValue The current percentage value of the progress circle, the percentage of the
* circle to fill.
* @param rotation The starting point of the circle with 0 being the 0 degree point.
* @return A string for an SVG path representing a circle filled from the starting point to the
* percentage value provided.
*/
function getSvgArc(currentValue, rotation) {
// The angle can't be exactly 360, because the arc becomes hidden.
var maximumAngle = 359.99 / 100;
var startPoint = rotation || 0;
var radius = 50;
var pathRadius = 40;
var startAngle = startPoint * maximumAngle;
var endAngle = currentValue * maximumAngle;
var start = polarToCartesian(radius, pathRadius, startAngle);
var end = polarToCartesian(radius, pathRadius, endAngle + startAngle);
var arcSweep = endAngle < 0 ? 0 : 1;
var largeArcFlag;
if (endAngle < 0) {
largeArcFlag = endAngle >= -180 ? 0 : 1;
}
else {
largeArcFlag = endAngle <= 180 ? 0 : 1;
}
return "M" + start + "A" + pathRadius + "," + pathRadius + " 0 " + largeArcFlag + "," + arcSweep + " " + end;
}
var MdProgressCircleModule = (function () {
function MdProgressCircleModule() {
}
MdProgressCircleModule.forRoot = function () {
return {
ngModule: MdProgressCircleModule,
providers: []
};
};
MdProgressCircleModule = __decorate([
_angular_core.NgModule({
exports: [MdProgressCircle, MdSpinner],
declarations: [MdProgressCircle, MdSpinner],
}),
__metadata('design:paramtypes', [])
], MdProgressCircleModule);
return MdProgressCircleModule;
}());
exports.MdProgressCircle = MdProgressCircle;
exports.MdSpinner = MdSpinner;
exports.MdProgressCircleModule = MdProgressCircleModule;
Object.defineProperty(exports, '__esModule', { value: true });
})));