mirror of
https://github.com/material-components/material-web.git
synced 2026-02-04 00:54:16 +08:00
BREAKING CHANGE: Sass component tokens, such as `tokens.md-comp-checkbox-values()`, return `var(--md-<component>, <value>)` instead of just the CSS value. Use `$exclude-custom-properties: true` to remove them. PiperOrigin-RevId: 652550625
288 lines
7.9 KiB
SCSS
288 lines
7.9 KiB
SCSS
//
|
|
// Copyright 2023 Google LLC
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
// go/keep-sorted start
|
|
@use 'sass:list';
|
|
// go/keep-sorted end
|
|
// go/keep-sorted start
|
|
@use '../../tokens';
|
|
// go/keep-sorted end
|
|
|
|
@mixin theme($tokens) {
|
|
$supported-tokens: tokens.$md-comp-circular-progress-supported-tokens;
|
|
@each $token, $value in $tokens {
|
|
@if list.index($supported-tokens, $token) == null {
|
|
@error 'Token `#{$token}` is not a supported token.';
|
|
}
|
|
|
|
@if $value {
|
|
--md-circular-progress-#{$token}: #{$value};
|
|
}
|
|
}
|
|
}
|
|
|
|
@mixin styles() {
|
|
$tokens: tokens.md-comp-circular-progress-values();
|
|
|
|
// If changing this value, make sure to change $size-without-padding in the
|
|
// circular-progress tokens.
|
|
$container-padding: 4px;
|
|
|
|
// note, these value come from the m2 version but match current gm3 values.
|
|
// Constants:
|
|
// ARCSIZE = 270 degrees (amount of circle the arc takes up)
|
|
// ARCTIME = 1333ms (time it takes to expand and contract arc)
|
|
// ARCSTARTROT = 216 degrees (how much the start location of the arc
|
|
// should rotate each time, 216 gives us a
|
|
// 5 pointed star shape (it's 360/5 * 3).
|
|
// For a 7 pointed star, we might do
|
|
// 360/7 * 3 = 154.286)
|
|
// ARCTIME
|
|
$arc-duration: 1333ms;
|
|
// 4 * ARCTIME
|
|
$cycle-duration: calc(4 * $arc-duration);
|
|
// ARCTIME * 360 / (ARCSTARTROT + (360-ARCSIZE))
|
|
$linear-rotate-duration: calc($arc-duration * 360 / 306);
|
|
|
|
$indeterminate-easing: cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
|
:host {
|
|
@each $token, $value in $tokens {
|
|
--_#{$token}: #{$value};
|
|
}
|
|
|
|
display: inline-flex;
|
|
vertical-align: middle;
|
|
width: var(--_size);
|
|
height: var(--_size);
|
|
position: relative;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
// `contain` and `content-visibility` are performance optimizations
|
|
// important here because progress indicators are often used when a cpu
|
|
// intensive task is underway so it's especially important to minimize
|
|
// their cpu consumption.
|
|
contain: strict;
|
|
content-visibility: auto;
|
|
}
|
|
|
|
.progress {
|
|
flex: 1;
|
|
align-self: stretch;
|
|
margin: $container-padding;
|
|
}
|
|
|
|
.progress,
|
|
.spinner,
|
|
.left,
|
|
.right,
|
|
.circle,
|
|
svg,
|
|
.track,
|
|
.active-track {
|
|
position: absolute;
|
|
inset: 0;
|
|
}
|
|
|
|
svg {
|
|
transform: rotate(-90deg);
|
|
}
|
|
|
|
circle {
|
|
cx: 50%;
|
|
cy: 50%;
|
|
r: calc(50% * (1 - var(--_active-indicator-width) / 100));
|
|
// match size to indeterminate border width
|
|
stroke-width: calc(var(--_active-indicator-width) * 1%);
|
|
// note, pathLength is set so this can be normalized
|
|
stroke-dasharray: 100;
|
|
fill: transparent;
|
|
}
|
|
|
|
.active-track {
|
|
// note, these value come from the m2 version but match current gm3 values.
|
|
transition: stroke-dashoffset 500ms cubic-bezier(0, 0, 0.2, 1);
|
|
stroke: var(--_active-indicator-color);
|
|
}
|
|
|
|
.track {
|
|
stroke: transparent;
|
|
}
|
|
|
|
.progress.indeterminate {
|
|
animation: linear infinite linear-rotate;
|
|
animation-duration: $linear-rotate-duration;
|
|
}
|
|
|
|
.spinner {
|
|
animation: infinite both rotate-arc;
|
|
animation-duration: $cycle-duration;
|
|
animation-timing-function: $indeterminate-easing;
|
|
}
|
|
|
|
.left {
|
|
overflow: hidden;
|
|
inset: 0 50% 0 0;
|
|
}
|
|
|
|
.right {
|
|
overflow: hidden;
|
|
inset: 0 0 0 50%;
|
|
}
|
|
|
|
.circle {
|
|
box-sizing: border-box;
|
|
border-radius: 50%;
|
|
// match size to svg stroke width, which is a fraction of the overall
|
|
// padding box width.
|
|
$_padding-box-width: calc(var(--_size) - 2 * $container-padding);
|
|
$_active-indicator-fraction: calc(var(--_active-indicator-width) / 100);
|
|
border: solid calc($_active-indicator-fraction * $_padding-box-width);
|
|
border-color: var(--_active-indicator-color) var(--_active-indicator-color)
|
|
transparent transparent;
|
|
animation: expand-arc;
|
|
animation-iteration-count: infinite;
|
|
animation-fill-mode: both;
|
|
animation-duration: $arc-duration, $cycle-duration;
|
|
animation-timing-function: $indeterminate-easing;
|
|
}
|
|
|
|
.four-color .circle {
|
|
animation-name: expand-arc, four-color;
|
|
}
|
|
|
|
.left .circle {
|
|
rotate: 135deg;
|
|
inset: 0 -100% 0 0;
|
|
}
|
|
.right .circle {
|
|
rotate: 100deg;
|
|
inset: 0 0 0 -100%;
|
|
animation-delay: calc(-0.5 * $arc-duration), 0ms;
|
|
}
|
|
|
|
@media (forced-colors: active) {
|
|
.active-track {
|
|
stroke: CanvasText;
|
|
}
|
|
|
|
.circle {
|
|
border-color: CanvasText CanvasText Canvas Canvas;
|
|
}
|
|
}
|
|
|
|
// Indeterminate mode is 3 animations composed together:
|
|
// 1. expand-arc: an arc is expanded/contracted between 10deg and 270deg.
|
|
// 2. rotate-arc: at the same time, the arc is rotated in increments
|
|
// of 270deg.
|
|
// 3. linear-rotate: that rotating arc is then linearly rotated to produce
|
|
// a spinning expanding/contracting arc.
|
|
//
|
|
// See original implementation:
|
|
// https://github.com/PolymerElements/paper-spinner/blob/master/paper-spinner-styles.js.
|
|
|
|
// 1. expand-arc
|
|
// This is used on 2 divs which each represent half the desired
|
|
// 270deg arc with one offset by 50%. This creates an arc which expands from
|
|
// 10deg to 270deg.
|
|
@keyframes expand-arc {
|
|
0% {
|
|
transform: rotate(265deg);
|
|
}
|
|
50% {
|
|
transform: rotate(130deg);
|
|
}
|
|
100% {
|
|
transform: rotate(265deg);
|
|
}
|
|
}
|
|
|
|
// 2. rotate-arc
|
|
// The arc seamlessly travels around the circle indefinitely so it needs to
|
|
// end at a full rotation of the circle. This rotates the 270 deg
|
|
// (270/360 = 3/4) arc 4x (4 * 3/4 = 3) so it ends at
|
|
// (3 * 360 = 1080).
|
|
// This is sub-divided into increments of 135deg since the arc is rendered
|
|
// with 2 divs acting together.
|
|
@keyframes rotate-arc {
|
|
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);
|
|
}
|
|
}
|
|
|
|
// 3. linear-rotate
|
|
// The traveling expanding arc is linearly rotated to produce the spinner
|
|
// effect.
|
|
@keyframes linear-rotate {
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
// This animates between 4 colors which are each shown for 25% of the time.
|
|
// Each color is shown solid for 3/5 of that time (3/5 * 25% = 15%) and
|
|
// transitions to the next color for 2/5 of that time (2/5 * 25% = 10%).
|
|
@keyframes four-color {
|
|
0% {
|
|
border-top-color: var(--_four-color-active-indicator-one-color);
|
|
border-right-color: var(--_four-color-active-indicator-one-color);
|
|
}
|
|
15% {
|
|
border-top-color: var(--_four-color-active-indicator-one-color);
|
|
border-right-color: var(--_four-color-active-indicator-one-color);
|
|
}
|
|
25% {
|
|
border-top-color: var(--_four-color-active-indicator-two-color);
|
|
border-right-color: var(--_four-color-active-indicator-two-color);
|
|
}
|
|
40% {
|
|
border-top-color: var(--_four-color-active-indicator-two-color);
|
|
border-right-color: var(--_four-color-active-indicator-two-color);
|
|
}
|
|
50% {
|
|
border-top-color: var(--_four-color-active-indicator-three-color);
|
|
border-right-color: var(--_four-color-active-indicator-three-color);
|
|
}
|
|
65% {
|
|
border-top-color: var(--_four-color-active-indicator-three-color);
|
|
border-right-color: var(--_four-color-active-indicator-three-color);
|
|
}
|
|
75% {
|
|
border-top-color: var(--_four-color-active-indicator-four-color);
|
|
border-right-color: var(--_four-color-active-indicator-four-color);
|
|
}
|
|
90% {
|
|
border-top-color: var(--_four-color-active-indicator-four-color);
|
|
border-right-color: var(--_four-color-active-indicator-four-color);
|
|
}
|
|
100% {
|
|
border-top-color: var(--_four-color-active-indicator-one-color);
|
|
border-right-color: var(--_four-color-active-indicator-one-color);
|
|
}
|
|
}
|
|
}
|