Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit b1f7dc4

Browse files
fix(progressCircular): css reduction with js animations
reduce the generated css by removing the scss for loops for animations of determinate indicators; use JS animations. * enable animation disables simply by setting md-mode = "" * hide div.md-spinner-wrapper when the md-mode is empty or not valid * use class selectors instead of attribute selectors (ie perfs) * create `animateIndicator()` for determinate indicators * remove SCSS for loops for css generation; ~54k reduction in CSS size BREAKING-CHANGES * ProgressCircular now uses Class selectors instead of `value` Attribute selectors. * `Determinate` animations are now JS based to set the CSS programmatically. Before: ```css md-progress-circular[value="1"] .md-inner .md-left .md-half-circle { -webkit-transform: rotate(135deg); transform: rotate(135deg); } md-progress-circular[value="1"] .md-inner .md-right .md-half-circle { transition: -webkit-transform 0.1s linear; transition: transform 0.1s linear; -webkit-transform: rotate(-131.4deg); transform: rotate(-131.4deg); } md-progress-circular[md-mode=indeterminate] .md-spinner-wrapper { -webkit-animation: outer-rotate 2.91667s linear infinite; } ``` Now: ```css // [Value]-based animation CSS generated in JS md-progress-circular.md-mode-indeterminate .md-spinner-wrapper { -webkit-animation: outer-rotate 2.91667s linear infinite; } ```
1 parent 303ab0d commit b1f7dc4

File tree

5 files changed

+154
-121
lines changed

5 files changed

+154
-121
lines changed

src/components/progressCircular/demoBasicUsage/index.html

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,27 @@ <h4>Indeterminate</h4>
1414

1515
<h4>Theming </h4>
1616

17-
<p>Use `md-warn` or `md-accent` to apply theme colors to your loader.</p>
17+
<p>Your current theme colors can be used to easily colorize your progress indicator with `md-warn` or `md-accent` colors:</p>
1818
<div layout="row" layout-sm="column" layout-align="space-around" >
1919
<md-progress-circular class="md-hue-2" md-mode="{{vm.modes[0]}}" md-diameter="20px" ></md-progress-circular>
2020
<md-progress-circular class="md-accent" md-mode="{{vm.modes[1]}}" md-diameter="40" ></md-progress-circular>
21-
<md-progress-circular class="md-accent md-hue-1" md-mode="{{vm.modes[2]}}" md-diameter="60" ></md-progress-circular>
21+
<md-progress-circular class="md-accent md-hue-1" md-mode="{{vm.modes[2]}}" md-diameter="60" ></md-progress-circular>
2222
<md-progress-circular class="md-warn md-hue-3" md-mode="{{vm.modes[3]}}" md-diameter="70"></md-progress-circular>
2323
<md-progress-circular md-mode="{{vm.modes[4]}}" md-diameter="96"></md-progress-circular>
2424
</div>
2525

26-
<div layout="row" id="loaders">
26+
<div layout="row" id="loaders">
2727

28-
<p style="margin-right: 20px">Show Progress Circular Indicators: </p>
28+
<p style="margin-right: 20px">Show Progress Circular Indicators: </p>
2929

30-
<h5>Off</h5>
31-
<md-switch
32-
ng-model="vm.activated"
33-
ng-change="vm.toggleActivation()"
34-
style=""
35-
aria-label="Toggle Progress Circular Indicators">
36-
<h5>On</h5>
37-
</md-switch>
38-
</div>
30+
<h5>Off</h5>
31+
<md-switch
32+
ng-model="vm.activated"
33+
ng-change="vm.toggleActivation()"
34+
aria-label="Toggle Progress Circular Indicators">
35+
<h5>On</h5>
36+
</md-switch>
37+
</div>
3938

4039

4140
</div>

src/components/progressCircular/demoBasicUsage/script.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,16 @@ angular
44
function($scope, $interval) {
55
var self = this, j= 0, counter = 0;
66

7-
self.mode = 'query';
8-
self.modes = new Array(4);
7+
self.modes = [ ];
98
self.activated = true;
109
self.determinateValue = 30;
1110

1211
/**
1312
* Turn off or on the 5 themed loaders
1413
*/
1514
self.toggleActivation = function() {
16-
if ( !self.activated ) self.modes = [ ];
17-
if ( self.activated ) j = counter = 0;
15+
if ( !self.activated ) self.modes = [ ];
16+
if ( self.activated ) j = counter = 0;
1817
};
1918

2019
// Iterate every 100ms, non-stop

src/components/progressCircular/progress-circular.js

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,17 @@ angular.module('material.components.progressCircular', [
4444
* <md-progress-circular md-mode="indeterminate"></md-progress-circular>
4545
* </hljs>
4646
*/
47-
function MdProgressCircularDirective($mdConstant, $mdTheming) {
47+
function MdProgressCircularDirective($mdConstant, $mdTheming, $mdUtil) {
4848
var DEFAULT_PROGRESS_SIZE = 100;
4949
var DEFAULT_SCALING = 0.5;
5050

51+
var MODE_DETERMINATE = "determinate",
52+
MODE_INDETERMINATE = "indeterminate";
53+
54+
5155
return {
5256
restrict: 'E',
57+
scope : true,
5358
template:
5459
// The progress 'circle' is composed of two half-circles: the left side and the right
5560
// side. Each side has CSS applied to 'fill-in' the half-circle to the appropriate progress.
@@ -79,19 +84,79 @@ function MdProgressCircularDirective($mdConstant, $mdTheming) {
7984

8085
function postLink(scope, element, attr) {
8186
$mdTheming(element);
87+
8288
var circle = element[0];
89+
var spinnerWrapper = angular.element(element.children()[0]);
90+
91+
var lastMode, toVendorCSS = $mdUtil.dom.animator.toCss;
92+
93+
// Update size/scaling of the progress indicator
94+
// Watch the "value" and "md-mode" attributes
95+
8396
circle.style[$mdConstant.CSS.TRANSFORM] = 'scale(' + getDiameterRatio() + ')';
8497

8598
attr.$observe('value', function(value) {
8699
var percentValue = clamp(value);
87100
element.attr('aria-valuenow', percentValue);
101+
102+
if (attr.mdMode == "determinate") {
103+
animateIndicator(percentValue);
104+
}
88105
});
89106

90-
var spinnerWrapper = angular.element(element.children()[0]);
91107
attr.$observe('mdMode',function(mode){
92-
spinnerWrapper[mode ? 'removeClass' : 'addClass']('ng-hide');
108+
switch( mode ) {
109+
case MODE_DETERMINATE:
110+
case MODE_INDETERMINATE:
111+
spinnerWrapper.removeClass('ng-hide');
112+
113+
// Inject class selector instead of attribute selector
114+
// (@see layout.js changes for IE performance issues)
115+
116+
if ( lastMode ) spinnerWrapper.removeClass( lastMode );
117+
lastMode = "md-mode-" + mode;
118+
if ( lastMode ) spinnerWrapper.addClass( lastMode );
119+
120+
break;
121+
default:
122+
spinnerWrapper.addClass('ng-hide');
123+
}
93124
});
94125

126+
var leftC, rightC, gap;
127+
128+
/**
129+
* Manually animate the Determinate indicator based on the specified
130+
* percentage value (0-100).
131+
*
132+
* Note: this animation was previously done using SCSS.
133+
* - generated 54K of styles
134+
* - use attribute selectors which had poor performances in IE
135+
*/
136+
function animateIndicator(value) {
137+
leftC = leftC || angular.element(element[0].querySelector('.md-left > .md-half-circle'));
138+
rightC = rightC || angular.element(element[0].querySelector('.md-right > .md-half-circle'));
139+
gap = gap || angular.element(element[0].querySelector('.md-gap'));
140+
141+
var gapStyles = removeEmptyValues({
142+
borderBottomColor: (value <= 50) ? "transparent !important" : "",
143+
transition: (value <= 50) ? "" : "borderBottomColor 0.1s linear"
144+
}),
145+
leftStyles = removeEmptyValues({
146+
transition: (value <= 50) ? "transform 0.1s linear" : "",
147+
transform: $mdUtil.supplant("rotate({0}deg)", [value <= 50 ? 135 : ((((value - 50) / 50) * 180) + 135)])
148+
}),
149+
rightStyles = removeEmptyValues({
150+
transition: (value >= 50) ? "transform 0.1s linear" : "",
151+
transform: $mdUtil.supplant("rotate({0}deg)", [value >= 50 ? 45 : (value / 50 * 180 - 135)])
152+
});
153+
154+
leftC.css(toVendorCSS(leftStyles));
155+
rightC.css(toVendorCSS(rightStyles));
156+
gap.css(toVendorCSS(gapStyles));
157+
158+
}
159+
95160
/**
96161
* We will scale the progress circle based on the default diameter.
97162
*
@@ -102,9 +167,7 @@ function MdProgressCircularDirective($mdConstant, $mdTheming) {
102167
if ( !attr.mdDiameter ) return DEFAULT_SCALING;
103168

104169
var match = /([0-9]*)%/.exec(attr.mdDiameter);
105-
var value = match && match[1]/100;
106-
107-
value = Math.max(0, value || parseFloat(attr.mdDiameter));
170+
var value = Math.max(0, (match && match[1]/100) || parseFloat(attr.mdDiameter));
108171

109172
// should return ratio; DEFAULT_PROGRESS_SIZE === 100px is default size
110173
return (value > 1) ? value / DEFAULT_PROGRESS_SIZE : value;
@@ -119,4 +182,14 @@ function MdProgressCircularDirective($mdConstant, $mdTheming) {
119182
function clamp(value) {
120183
return Math.max(0, Math.min(value || 0, 100));
121184
}
185+
186+
function removeEmptyValues(target) {
187+
for (var key in target) {
188+
if (target.hasOwnProperty(key)) {
189+
if ( target[key] == "" ) delete target[key];
190+
}
191+
}
192+
193+
return target;
194+
}
122195
}

src/components/progressCircular/progress-circular.scss

Lines changed: 57 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,6 @@ $progress-circular-sporadic-duration : $progress-circular-duration !default;
66
$progress-border-width : 10px;
77
$progress-circular-size : 10 * $progress-border-width !default;
88

9-
@keyframes outer-rotate {
10-
100% { transform: rotate(360deg); }
11-
}
12-
@keyframes left-wobble {
13-
0%, 100% { transform: rotate(130deg); }
14-
50% { transform: rotate( -5deg); }
15-
}
16-
@keyframes right-wobble {
17-
0%, 100% { transform: rotate(-130deg); }
18-
50% { transform: rotate( 5deg); }
19-
}
20-
@keyframes sporadic-rotate {
21-
12.5% { transform: rotate( 135deg); }
22-
25% { transform: rotate( 270deg); }
23-
37.5% { transform: rotate( 405deg); }
24-
50% { transform: rotate( 540deg); }
25-
62.5% { transform: rotate( 675deg); }
26-
75% { transform: rotate( 810deg); }
27-
87.5% { transform: rotate( 945deg); }
28-
100% { transform: rotate(1080deg); }
29-
}
30-
319
md-progress-circular {
3210
width: $progress-circular-size;
3311
height: $progress-circular-size;
@@ -86,79 +64,32 @@ md-progress-circular {
8664
}
8765
}
8866

89-
// TODO: This while-loop generates about 2 kilobytes of css after gzip.
90-
// Refactor pr ogressCircular to animate with javascript.
91-
$i: 0;
92-
@while $i <= 100 {
93-
&[value="#{$i}"] {
94-
.md-inner {
95-
.md-left {
96-
.md-half-circle {
97-
@if $i < 50 {
98-
transform: rotate(135deg);
99-
} @else {
100-
transition: transform 0.1s linear;
101-
$deg: ((($i - 50) / 50) * 180) + 135;
102-
transform: rotate(#{$deg}deg);
103-
}
104-
}
105-
}
106-
.md-right {
107-
.md-half-circle {
108-
@if $i <= 50 {
109-
transition: transform 0.1s linear;
110-
$deg: $i / 50 * 180 - 135;
111-
transform: rotate(#{$deg}deg);
112-
} @else {
113-
transform: rotate(45deg);
114-
}
115-
}
116-
}
117-
.md-gap {
118-
border-bottom-width: $progress-border-width;
119-
border-bottom-style: solid;
120-
@if $i <= 50 {
121-
border-bottom-color: transparent !important;
122-
} @else {
123-
transition: border-bottom-color 0.1s linear;
124-
}
67+
.md-spinner-wrapper.md-mode-indeterminate {
68+
animation: outer-rotate $progress-circular-outer-duration linear infinite;
69+
.md-inner {
70+
animation: sporadic-rotate $progress-circular-sporadic-duration $progress-circular-ease-in-out infinite;
71+
.md-left, .md-right {
72+
.md-half-circle {
73+
animation-iteration-count: infinite;
74+
animation-duration: ($progress-circular-duration * 0.25);
75+
animation-timing-function: $progress-circular-ease-in-out;
12576
}
12677
}
127-
}
128-
$i: $i + 1;
129-
}
130-
131-
&[md-mode=indeterminate], .md-mode-indeterminate {
132-
.md-spinner-wrapper {
133-
animation: outer-rotate $progress-circular-outer-duration linear infinite;
134-
.md-inner {
135-
animation: sporadic-rotate $progress-circular-sporadic-duration $progress-circular-ease-in-out infinite;
136-
.md-left, .md-right {
137-
.md-half-circle {
138-
animation-iteration-count: infinite;
139-
animation-duration: ($progress-circular-duration * 0.25);
140-
animation-timing-function: $progress-circular-ease-in-out;
141-
}
142-
}
143-
.md-left {
144-
.md-half-circle {
145-
animation-name: left-wobble;
146-
}
78+
.md-left {
79+
.md-half-circle {
80+
animation-name: left-wobble;
14781
}
148-
.md-right {
149-
.md-half-circle {
150-
animation-name: right-wobble;
151-
}
82+
}
83+
.md-right {
84+
.md-half-circle {
85+
animation-name: right-wobble;
15286
}
15387
}
15488
}
15589
}
156-
}
15790

158-
.ng-hide md-progress-circular,
159-
md-progress-circular.ng-hide, {
160-
&[md-mode=indeterminate], .md-mode-indeterminate {
161-
.md-spinner-wrapper {
91+
.ng-hide md-progress-circular, md-progress-circular.ng-hide {
92+
.md-spinner-wrapper {
16293
animation: none;
16394
.md-inner {
16495
animation: none;
@@ -175,21 +106,49 @@ md-progress-circular.ng-hide, {
175106
}
176107
}
177108
}
178-
}
179109

180-
.md-spinner-wrapper.ng-hide {
181-
animation: none;
182-
.md-inner {
110+
.md-spinner-wrapper.ng-hide {
183111
animation: none;
184-
.md-left {
185-
.md-half-circle {
186-
animation-name: none;
112+
.md-inner {
113+
animation: none;
114+
.md-left {
115+
.md-half-circle {
116+
animation-name: none;
117+
}
187118
}
188-
}
189-
.md-right {
190-
.md-half-circle {
191-
animation-name: none;
119+
.md-right {
120+
.md-half-circle {
121+
animation-name: none;
122+
}
192123
}
193124
}
194125
}
126+
127+
}
128+
129+
130+
//
131+
// Keyframe animation for the Indeterminate Progress
132+
//
133+
@keyframes outer-rotate {
134+
100% { transform: rotate(360deg); }
195135
}
136+
@keyframes left-wobble {
137+
0%, 100% { transform: rotate(130deg); }
138+
50% { transform: rotate( -5deg); }
139+
}
140+
@keyframes right-wobble {
141+
0%, 100% { transform: rotate(-130deg); }
142+
50% { transform: rotate( 5deg); }
143+
}
144+
@keyframes sporadic-rotate {
145+
12.5% { transform: rotate( 135deg); }
146+
25% { transform: rotate( 270deg); }
147+
37.5% { transform: rotate( 405deg); }
148+
50% { transform: rotate( 540deg); }
149+
62.5% { transform: rotate( 675deg); }
150+
75% { transform: rotate( 810deg); }
151+
87.5% { transform: rotate( 945deg); }
152+
100% { transform: rotate(1080deg); }
153+
}
154+

src/core/util/animation/animate.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ function AnimateDomUtils($mdUtil, $q, $timeout, $mdConstant, $animateCss) {
124124
css[key] = value + 'px';
125125
} else {
126126
switch (key) {
127+
case 'transition':
128+
convertToVendor(key, $mdConstant.CSS.TRANSITION, value);
129+
break;
127130
case 'transform':
128131
convertToVendor(key, $mdConstant.CSS.TRANSFORM, value);
129132
break;

0 commit comments

Comments
 (0)