Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | 36x 36x 36x 36x 37x 20x 36x 36x 36x 36x 36x 37x 37x 37x | <script setup lang="ts">
import { computed } from "vue";
import { toCssSize, toPx } from "../../../utils/size";
import { resolveTrack } from "../track";
import { clampProgress } from "../../../utils/progress";
interface Props {
size?: number | string;
color?: string;
speed?: number;
thickness?: number;
track?: boolean | string;
/** 0–100; anything non-finite keeps the arc indeterminate. */
value?: number;
}
const props = withDefaults(defineProps<Props>(), {
size: 40,
color: "currentColor",
speed: 1,
thickness: 4,
track: false,
});
const box = computed(() => toCssSize(props.size));
const trackInfo = computed(() => resolveTrack(props.track));
const progress = computed(() => clampProgress(props.value));
const speed = computed(() => (props.speed > 0 ? props.speed : 1));
// The bar uses pathLength="100", so dash lengths are percentages of the
// circle and `value` maps straight onto stroke-dasharray.
/* The viewBox is 44 units wide. `thickness` is in px (like circle/ring),
so convert it to viewBox units when the size is known in px; for other
units (rem, %) fall back to 1 unit = 1px of a 44px box. The radius
shrinks by half the stroke so a thick stroke never clips at the edge. */
const VIEWBOX = 44;
const strokeWidth = computed(() => {
const px = toPx(props.size);
return px ? (props.thickness * VIEWBOX) / px : props.thickness;
});
const radius = computed(() => Math.max(1, VIEWBOX / 2 - strokeWidth.value / 2));
</script>
<template>
<svg
class="vslk-spinner-arc"
:class="{ 'vslk-spinner-arc--indeterminate': progress === null }"
viewBox="0 0 44 44"
:style="{
width: box,
height: box,
color: props.color,
'--vslk-arc-rotate': `${2 / speed}s`,
'--vslk-arc-dash': `${1.5 / speed}s`,
}"
>
<circle
v-if="trackInfo"
class="vslk-spinner-arc__track"
:data-vslk-track="trackInfo.attr"
cx="22"
cy="22"
:r="radius"
fill="none"
:stroke="trackInfo.color"
:stroke-width="strokeWidth"
/>
<circle
class="vslk-spinner-arc__bar"
cx="22"
cy="22"
:r="radius"
fill="none"
stroke="currentColor"
:stroke-width="strokeWidth"
:stroke-linecap="progress === 0 ? 'butt' : 'round'"
pathLength="100"
:style="progress === null ? undefined : { strokeDasharray: `${progress} 100` }"
/>
</svg>
</template>
<style scoped>
.vslk-spinner-arc {
display: block;
max-width: 100%;
flex-shrink: 0;
/* determinate: start at 12 o'clock and fill clockwise */
transform: rotate(-90deg);
}
.vslk-spinner-arc__bar {
transition: stroke-dasharray 0.3s ease;
}
/* Indeterminate (Material style): the whole circle turns while the arc
grows and shrinks, so it never looks like a fixed shape spinning. */
.vslk-spinner-arc--indeterminate {
animation: vslk-arc-rotate var(--vslk-arc-rotate) linear infinite;
}
.vslk-spinner-arc--indeterminate .vslk-spinner-arc__bar {
animation: vslk-arc-dash var(--vslk-arc-dash) ease-in-out infinite;
}
@keyframes vslk-arc-rotate {
from {
transform: rotate(-90deg);
}
to {
transform: rotate(270deg);
}
}
@keyframes vslk-arc-dash {
0% {
stroke-dasharray: 1 150;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 70 150;
stroke-dashoffset: -28;
}
100% {
stroke-dasharray: 70 150;
stroke-dashoffset: -99;
}
}
</style>
|