Skip to content

feat(progress-spinner): switch to css-based animation #7265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/demo-app/progress-spinner/progress-spinner-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ <h1>Determinate</h1>

<div class="demo-progress-spinner">
<md-progress-spinner [mode]="isDeterminate ? 'determinate' : 'indeterminate'"
[value]="progressValue" color="primary" [strokeWidth]="1"></md-progress-spinner>
[value]="progressValue" color="primary" [strokeWidth]="1" [diameter]="32"></md-progress-spinner>
<md-progress-spinner [mode]="isDeterminate ? 'determinate' : 'indeterminate'"
[value]="progressValue" color="accent"></md-progress-spinner>
[value]="progressValue" color="accent" [diameter]="50"></md-progress-spinner>
</div>

<h1>Indeterminate</h1>
Expand Down
6 changes: 3 additions & 3 deletions src/lib/progress-spinner/_progress-spinner-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
$warn: map-get($theme, warn);

.mat-progress-spinner, .mat-spinner {
path {
circle {
stroke: mat-color($primary);
}

&.mat-accent path {
&.mat-accent circle {
stroke: mat-color($accent);
}

&.mat-warn path {
&.mat-warn circle {
stroke: mat-color($warn);
}
}
Expand Down
1 change: 1 addition & 0 deletions src/lib/progress-spinner/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
* found in the LICENSE file at https://angular.io/license
*/


export * from './public_api';
2 changes: 0 additions & 2 deletions src/lib/progress-spinner/mat-exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@
import {
MdProgressSpinner,
MdProgressSpinnerBase,
MdProgressSpinnerCssMatStyler,
MdSpinner,
} from './progress-spinner';
import {MdProgressSpinnerModule} from './progress-spinner-module';


export {MdProgressSpinner as MatProgressSpinner};
export {MdProgressSpinnerBase as MatProgressSpinnerBase};
export {MdProgressSpinnerCssMatStyler as MatProgressSpinnerCssMatStyler};
export {MdProgressSpinnerModule as MatProgressSpinnerModule};
export {MdSpinner as MatSpinner};
10 changes: 4 additions & 6 deletions src/lib/progress-spinner/progress-spinner-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,24 @@
*/

import {NgModule} from '@angular/core';
import {PlatformModule} from '@angular/cdk/platform';
import {MdCommonModule} from '@angular/material/core';
import {
MdProgressSpinner,
MdSpinner,
MdProgressSpinnerCssMatStyler,
} from './progress-spinner';


@NgModule({
imports: [MdCommonModule],
imports: [MdCommonModule, PlatformModule],
exports: [
MdProgressSpinner,
MdSpinner,
MdCommonModule,
MdProgressSpinnerCssMatStyler
MdCommonModule
],
declarations: [
MdProgressSpinner,
MdSpinner,
MdProgressSpinnerCssMatStyler
MdSpinner
],
})
export class MdProgressSpinnerModule {}
20 changes: 16 additions & 4 deletions src/lib/progress-spinner/progress-spinner.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,20 @@
element containing the SVG. `focusable="false"` prevents IE from allowing the user to
tab into the SVG element.
-->
<svg viewBox="0 0 100 100"
preserveAspectRatio="xMidYMid meet"
focusable="false">
<path #path [style.strokeWidth]="strokeWidth"></path>

<svg
[style.width.px]="_diameter"
[style.height.px]="_diameter"
[attr.viewBox]="_viewBox"
preserveAspectRatio="xMidYMid meet"
focusable="false">

<circle
cx="50%"
cy="50%"
[attr.r]="_circleRadius"
[style.stroke-dashoffset.px]="_strokeDashOffset"
[style.stroke-dasharray.px]="_strokeCircumference"
[style.transform.rotate]="'360deg'"
[style.stroke-width.px]="strokeWidth"></circle>
</svg>
118 changes: 77 additions & 41 deletions src/lib/progress-spinner/progress-spinner.scss
Original file line number Diff line number Diff line change
@@ -1,51 +1,53 @@
@import '../core/style/variables';


// Animation Durations
$mat-progress-spinner-duration: 5250ms !default;
$mat-progress-spinner-constant-rotate-duration: $mat-progress-spinner-duration * 0.55 !default;
$mat-progress-spinner-sporadic-rotate-duration: $mat-progress-spinner-duration !default;

// Component sizing
$mat-progress-spinner-stroke-width: 10px !default;
// Height and weight of the viewport for mat-progress-spinner.
$mat-progress-spinner-viewport-size: 100px !default;
// Animation config
$mat-progress-spinner-stroke-rotate-fallback-duration: 10 * 1000ms !default;
$mat-progress-spinner-stroke-rotate-fallback-ease: cubic-bezier(0.87, 0.03, 0.33, 1) !default;

$_mat-progress-spinner-default-radius: 45px;
$_mat-progress-spinner-default-circumference: $pi * $_mat-progress-spinner-default-radius * 2;

.mat-progress-spinner {
display: block;
// Height and width are provided for mat-progress-spinner to act as a default.
// The height and width are expected to be overwritten by application css.
height: $mat-progress-spinner-viewport-size;
width: $mat-progress-spinner-viewport-size;
overflow: hidden;

// 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.
position: relative;

svg {
height: 100%;
width: 100%;
position: absolute;
transform: translate(-50%, -50%) rotate(-90deg);
top: 50%;
left: 50%;
transform-origin: center;
overflow: visible;
}


path {
circle {
fill: transparent;
transform-origin: center;
transition: stroke-dashoffset 225ms linear;
}

&.mat-progress-spinner-indeterminate-animation[mode='indeterminate'] {
animation: mat-progress-spinner-linear-rotate $swift-ease-in-out-duration * 4
linear infinite;

transition: stroke $swift-ease-in-duration $ease-in-out-curve-function;
&.mat-progress-spinner-100 circle {
// Note: we multiply the duration by 8, because the animation is spread out in 8 stages.
animation: mat-progress-spinner-stroke-rotate-100 $swift-ease-in-out-duration * 8
$ease-in-out-curve-function infinite;
transition-property: stroke;
}
}

&.mat-progress-spinner-indeterminate-fallback-animation[mode='indeterminate'] {
animation: mat-progress-spinner-stroke-rotate-fallback
$mat-progress-spinner-stroke-rotate-fallback-duration
$mat-progress-spinner-stroke-rotate-fallback-ease
infinite;

&[mode='indeterminate'] svg {
animation-duration: $mat-progress-spinner-sporadic-rotate-duration,
$mat-progress-spinner-constant-rotate-duration;
animation-name: mat-progress-spinner-sporadic-rotate,
mat-progress-spinner-linear-rotate;
animation-timing-function: $ease-in-out-curve-function,
linear;
animation-iteration-count: infinite;
transition: none;
circle {
transition-property: stroke;
}
}
}

Expand All @@ -55,13 +57,47 @@ $mat-progress-spinner-viewport-size: 100px !default;
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes mat-progress-spinner-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); }

@at-root {
$start: (1 - 0.05) * $_mat-progress-spinner-default-circumference; // start the animation at 5%
$end: (1 - 0.8) * $_mat-progress-spinner-default-circumference; // end the animation at 80%
$fallback-iterations: 4;

@keyframes mat-progress-spinner-stroke-rotate-100 {
/*
stylelint-disable declaration-block-single-line-max-declarations,
declaration-block-semicolon-space-after
*/
0% { stroke-dashoffset: $start; transform: rotate(0); }
12.5% { stroke-dashoffset: $end; transform: rotate(0); }
12.51% { stroke-dashoffset: $end; transform: rotateX(180deg) rotate(72.5deg); }
25% { stroke-dashoffset: $start; transform: rotateX(180deg) rotate(72.5deg); }

25.1% { stroke-dashoffset: $start; transform: rotate(270deg); }
37.5% { stroke-dashoffset: $end; transform: rotate(270deg); }
37.51% { stroke-dashoffset: $end; transform: rotateX(180deg) rotate(161.5deg); }
50% { stroke-dashoffset: $start; transform: rotateX(180deg) rotate(161.5deg); }

50.01% { stroke-dashoffset: $start; transform: rotate(180deg); }
62.5% { stroke-dashoffset: $end; transform: rotate(180deg); }
62.51% { stroke-dashoffset: $end; transform: rotateX(180deg) rotate(251.5deg); }
75% { stroke-dashoffset: $start; transform: rotateX(180deg) rotate(251.5deg); }

75.01% { stroke-dashoffset: $start; transform: rotate(90deg); }
87.5% { stroke-dashoffset: $end; transform: rotate(90deg); }
87.51% { stroke-dashoffset: $end; transform: rotateX(180deg) rotate(341.5deg); }
100% { stroke-dashoffset: $start; transform: rotateX(180deg) rotate(341.5deg); }
// stylelint-enable
}

// For IE11 and Edge, we fall back to simply rotating the spinner because
// animating stroke-dashoffset is not supported. The fallback uses multiple
// iterations to vary where the spin "lands".
@keyframes mat-progress-spinner-stroke-rotate-fallback {
@for $i from 0 through $fallback-iterations {
$percent: 100 / $fallback-iterations * $i;
$offset: 360 / $fallback-iterations;
#{$percent}% { transform: rotate(#{$i * (360 * 3 + $offset)}deg); }
}
}
}
Loading