Skip to content

refactor(material/progress-spinner): clean up IE workarounds #23283

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

Merged
merged 1 commit into from
Aug 4, 2021
Merged
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
3 changes: 1 addition & 2 deletions src/material/progress-spinner/progress-spinner.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<!--
preserveAspectRatio of xMidYMid meet as the center of the viewport is the circle's
center. The center of the circle will remain at the center of the mat-progress-spinner
element containing the SVG. `focusable="false"` prevents IE from allowing the user to
tab into the SVG element.
element containing the SVG.
-->
<!--
All children need to be hidden for screen readers in order to support ChromeVox.
Expand Down
38 changes: 3 additions & 35 deletions src/material/progress-spinner/progress-spinner.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
@use '../../cdk/a11y';


// Animation config
$stroke-rotate-fallback-duration: 10 * 1000ms !default;
$stroke-rotate-fallback-ease: cubic-bezier(0.87, 0.03, 0.33, 1) !default;

$_default-radius: 45px;
$_default-circumference: variables.$pi * $_default-radius * 2;

Expand Down Expand Up @@ -35,10 +31,9 @@ $_default-circumference: variables.$pi * $_default-radius * 2;

@include a11y.high-contrast(active, off) {
// SVG colors aren't inverted automatically in high contrast mode. Set the
// stroke to currentColor in order to respect the user's color settings.
stroke: currentColor;
// currentColor blends in with the background in Chromium-based browsers
// so we have to fall back to `CanvasText` which isn't supported on IE.
// stroke to `CanvasText` in order to respect the user's color settings.
// Note that we use `CanvasText` instead of `currentColor`, because
// the latter blends in with the background on Chromium browsers.
stroke: CanvasText;
}
}
Expand All @@ -59,21 +54,6 @@ $_default-circumference: variables.$pi * $_default-radius * 2;
animation-iteration-count: infinite;
}
}

&.mat-progress-spinner-indeterminate-fallback-animation[mode='indeterminate'] {
svg {
@include private.private-animation-noop();
animation: mat-progress-spinner-stroke-rotate-fallback
$stroke-rotate-fallback-duration
$stroke-rotate-fallback-ease
infinite;
}

circle {
@include private.private-animation-noop();
transition-property: stroke;
}
}
}


Expand All @@ -86,7 +66,6 @@ $_default-circumference: variables.$pi * $_default-radius * 2;
@at-root {
$start: (1 - 0.05) * $_default-circumference; // start the animation at 5%
$end: (1 - 0.8) * $_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
Expand Down Expand Up @@ -119,15 +98,4 @@ $_default-circumference: variables.$pi * $_default-radius * 2;
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: private.private-div(100, $fallback-iterations) * $i;
$offset: private.private-div(360, $fallback-iterations);
#{$percent}% { transform: rotate(#{$i * (360 * 3 + $offset)}deg); }
}
}
}
41 changes: 14 additions & 27 deletions src/material/progress-spinner/progress-spinner.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {TestBed, waitForAsync, inject} from '@angular/core/testing';
import {TestBed, waitForAsync} from '@angular/core/testing';
import {Component, ViewEncapsulation, ViewChild, ElementRef} from '@angular/core';
import {By} from '@angular/platform-browser';
import {Platform, _getShadowRoot, _supportsShadowDom} from '@angular/cdk/platform';
import {_getShadowRoot, _supportsShadowDom} from '@angular/cdk/platform';
import {CommonModule} from '@angular/common';
import {
MatProgressSpinnerModule,
Expand Down Expand Up @@ -158,13 +158,7 @@ describe('MatProgressSpinner', () => {
});

it('should add a style tag with the indeterminate animation to the document head when using a ' +
'non-default diameter', inject([Platform], (platform: Platform) => {
// On Edge and IE we use a fallback animation because the
// browser doesn't support animating SVG correctly.
if (platform.EDGE || platform.TRIDENT) {
return;
}

'non-default diameter', () => {
const fixture = TestBed.createComponent(ProgressSpinnerCustomDiameter);
fixture.componentInstance.diameter = 32;
fixture.detectChanges();
Expand All @@ -184,7 +178,7 @@ describe('MatProgressSpinner', () => {

expect(document.head.querySelectorAll('style[mat-spinner-animation="32"]').length).toBe(1);
expect(document.head.querySelectorAll('style[mat-spinner-animation="64"]').length).toBe(1);
}));
});

it('should allow floating point values for custom diameter', () => {
const fixture = TestBed.createComponent(ProgressSpinnerCustomDiameter);
Expand All @@ -207,26 +201,19 @@ describe('MatProgressSpinner', () => {
.toBe('0 0 25.75 25.75', 'Expected the custom diameter to be applied to the svg viewBox.');
});

it('should handle creating animation style tags based on a floating point diameter',
inject([Platform], (platform: Platform) => {
// On Edge and IE we use a fallback animation because the
// browser doesn't support animating SVG correctly.
if (platform.EDGE || platform.TRIDENT) {
return;
}

const fixture = TestBed.createComponent(IndeterminateSpinnerCustomDiameter);
it('should handle creating animation style tags based on a floating point diameter', () => {
const fixture = TestBed.createComponent(IndeterminateSpinnerCustomDiameter);

fixture.componentInstance.diameter = 32.5;
fixture.detectChanges();
fixture.componentInstance.diameter = 32.5;
fixture.detectChanges();

const circleElement = fixture.nativeElement.querySelector('circle');
const circleElement = fixture.nativeElement.querySelector('circle');

expect(circleElement.style.animationName).toBe('mat-progress-spinner-stroke-rotate-32_5',
'Expected the spinner circle element to have an animation name based on the custom diameter');
expect(document.head.querySelectorAll('style[mat-spinner-animation="32_5"]').length).toBe(1,
'Expected a style tag with the indeterminate animation to be attached to the document head');
}));
expect(circleElement.style.animationName).toBe('mat-progress-spinner-stroke-rotate-32_5',
'Expected the spinner circle element to have an animation name based on the custom diameter');
expect(document.head.querySelectorAll('style[mat-spinner-animation="32_5"]').length).toBe(1,
'Expected a style tag with the indeterminate animation to be attached to the document head');
});

it('should allow a custom stroke width', () => {
const fixture = TestBed.createComponent(ProgressSpinnerCustomStrokeWidth);
Expand Down
23 changes: 7 additions & 16 deletions src/material/progress-spinner/progress-spinner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ export class MatProgressSpinner extends _MatProgressSpinnerBase implements OnIni
private _diameter = BASE_SIZE;
private _value = 0;
private _strokeWidth: number;
private _fallbackAnimation = false;

/**
* Element to which we should add the generated style tags for the indeterminate animation.
Expand Down Expand Up @@ -159,7 +158,7 @@ export class MatProgressSpinner extends _MatProgressSpinnerBase implements OnIni
this._spinnerAnimationLabel = this._getSpinnerAnimationLabel();

// If this is set before `ngOnInit`, the style root may not have been resolved yet.
if (!this._fallbackAnimation && this._styleRoot) {
if (this._styleRoot) {
this._attachStyleNode();
}
}
Expand All @@ -186,7 +185,11 @@ export class MatProgressSpinner extends _MatProgressSpinnerBase implements OnIni
}

constructor(elementRef: ElementRef<HTMLElement>,
platform: Platform,
/**
* @deprecated `_platform` parameter no longer being used.
* @breaking-change 14.0.0
*/
_platform: Platform,
@Optional() @Inject(DOCUMENT) private _document: any,
@Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode: string,
@Inject(MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS)
Expand All @@ -203,7 +206,6 @@ export class MatProgressSpinner extends _MatProgressSpinnerBase implements OnIni
trackedDiameters.set(_document.head, new Set<number>([BASE_SIZE]));
}

this._fallbackAnimation = platform.EDGE || platform.TRIDENT;
this._noopAnimations = animationMode === 'NoopAnimations' &&
(!!defaults && !defaults._forceAnimations);

Expand All @@ -226,13 +228,7 @@ export class MatProgressSpinner extends _MatProgressSpinnerBase implements OnIni
// node is inside an `ngIf` and a ShadowDom-encapsulated component.
this._styleRoot = _getShadowRoot(element) || this._document.head;
this._attachStyleNode();

// On IE and Edge, we can't animate the `stroke-dashoffset`
// reliably so we fall back to a non-spec animation.
const animationClass =
`mat-progress-spinner-indeterminate${this._fallbackAnimation ? '-fallback' : ''}-animation`;

element.classList.add(animationClass);
element.classList.add('mat-progress-spinner-indeterminate-animation');
}

/** The radius of the spinner, adjusted for stroke width. */
Expand All @@ -257,11 +253,6 @@ export class MatProgressSpinner extends _MatProgressSpinnerBase implements OnIni
return this._getStrokeCircumference() * (100 - this._value) / 100;
}

// In fallback mode set the circle to 80% and rotate it with CSS.
if (this._fallbackAnimation && this.mode === 'indeterminate') {
return this._getStrokeCircumference() * 0.2;
}

return null;
}

Expand Down
3 changes: 2 additions & 1 deletion tools/public_api_guard/material/progress-spinner.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export function MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS_FACTORY(): MatProgressSpinn

// @public
export class MatProgressSpinner extends _MatProgressSpinnerBase implements OnInit, CanColor {
constructor(elementRef: ElementRef<HTMLElement>, platform: Platform, _document: any, animationMode: string, defaults?: MatProgressSpinnerDefaultOptions);
constructor(elementRef: ElementRef<HTMLElement>,
_platform: Platform, _document: any, animationMode: string, defaults?: MatProgressSpinnerDefaultOptions);
get diameter(): number;
set diameter(size: number);
_getCircleRadius(): number;
Expand Down