参考文档:
张鑫旭大佬的博客:https://www.zhangxinxu.com/study/201710/colorful-time-count-d...

image.png

改造后的效果:
image.png

源码:vue3 <script lang="ts" setup> 模式版本:

<template>
  <div
    class="config-progress"
    :style="`--size:${size}px; --circle-size:${circleSize}px;`"
  >
    <svg :width="size" :height="size" :viewBox="`0 0 ${size} ${size}`">
      <defs>
        <linearGradient id="color1">
          <stop
            v-for="(item, index) in color1"
            :key="index"
            :offset="index === 0 ? 0 : 1 / (color1.length - 1) * index"
            :style="`stop-color:${item}`"
          />
        </linearGradient>
        <linearGradient id="color2">
          <stop
            v-for="(item, index) in color2"
            :key="index"
            :offset="index === 0 ? 0 : 1 / (color2.length - 1) * index"
            :style="`stop-color:${item}`"
          />
        </linearGradient>
      </defs>
      <!-- 外层 圆点 -->
      <circle
        stroke="#C3C3C3"
        class="out-border"
        stroke-dasharray="1,5"
        v-if="showBorder"
      />
      <!-- 内层 圆点 -->
      <circle
        stroke="#C3C3C3"
        class="inner-border"
        stroke-dasharray="1,5"
        v-if="showBorder"
      />
      <!-- 背景色 -->
      <circle stroke="#DBDBDB" class="group placeholder" />
      <circle
        stroke="url(#color1)"
        class="group progress"
        stroke-linecap="round"
        :stroke-dasharray="`${value1}, ${perimeter}`"
        :transform="`rotate(-90,${size / 2},${size / 2})`"
      />
      <circle
        stroke="url(#color2)"
        class="group progress"
        stroke-linecap="round"
        :stroke-dasharray="`${value2}, ${perimeter}`"
        :transform="`rotate(90,${size / 2},${size / 2})`"
        v-if="percentage > 50"
      />
    </svg>
    <div class="shadow">
      <slot />
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, defineProps } from 'vue';

interface IProps {
  size?: number;
  circleSize?: number;
  colors?: string[];
  percentage: number;
  showBorder?: boolean;
}

const props = defineProps<IProps>({
    size: 250,
    color: ['#FF5353', '#FF7400 ', '#fff000', '#A4DC66', '#4BCCCC'],
    circleSize: 24,
    percentage: 0,
    showBorder: false
});

const outerBorderSize = computed(() => props.size * 0.012);
const outerBorderSpace = computed(() => props.size * 0.016);

const color1 = computed(() => [props.colors[2], props.colors[1], props.colors[0]]);
const color2 = computed(() => [props.colors[4], props.colors[4], props.colors[3], props.colors[2]]);

const perimeter = computed(() => {
  const r = props.size / 2 - outerBorderSize.value - outerBorderSpace.value - props.circleSize / 2;
  return 2 * Math.PI * r;
});

const value1 = computed(() => {
  const v = (props.percentage * perimeter.value) / 100;
  return v > perimeter.value ? perimeter.value : v;
});

const value2 = computed(() => {
  return ((props.percentage - 50) * perimeter.value) / 100;
});
</script>

<style scoped lang="scss">
.config-progress {
  width: var(--size);
  height: var(--size);
  --outer-border-length: calc(var(--size) * 0.012);
  --outer-border-space: calc(var(--size) * 0.016);
  position: relative;

  .shadow {
    position: absolute;
    top: calc(var(--outer-border-length) + var(--outer-border-space) + var(--circle-size));
    left: calc(var(--outer-border-length) + var(--outer-border-space) + var(--circle-size));
    width: calc(var(--size) - var(--outer-border-length) * 2 - var(--outer-border-space) * 2 - var(--circle-size) * 2);
    height: calc(var(--size) - var(--outer-border-length) * 2 - var(--outer-border-space) * 2 - var(--circle-size) * 2);
    border-radius: 50%;
    box-shadow: 0 0 calc(var(--circle-size) / 2) rgba(0, 0, 0, 0.25);
    padding: calc(var(--outer-border-length) + var(--outer-border-space));
    box-sizing: border-box;
  }

  svg {
    width: var(--size);
    height: var(--size);
    background-color: transparent;
  }

  svg {
    circle {
      fill: none;
      cx: calc(var(--size) / 2);
      cy: calc(var(--size) / 2);
    }

    .out-border {
      stroke-width: var(--outer-border-length);
      r: calc(var(--size) / 2 - var(--outer-border-length));
      transform-origin: 50% 50%;
    }

    .shadow {
      r: calc(var(--size) / 2 - var(--outer-border-length) / 2 - var(--outer-border-space) - var(--circle-size));
      fill: rgba(0, 0, 0, 1);
    }

    .group {
      r: calc(var(--size) / 2 - var(--outer-border-length) - var(--outer-border-space) - var(--circle-size) / 2);
      stroke-width: var(--circle-size);
    }

    .inner-border {
      stroke-width: var(--outer-border-length);
      r: calc(var(--size) / 2 - var(--outer-border-length) - var(--outer-border-space) * 2 - var(--circle-size));
      transform-origin: 50% 50%;
    }

    .progress {
    }
  }
}

@keyframes rotate {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

@keyframes rotate2 {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(-360deg);
  }
}
</style>

调用:

    <CirclePercentage :percentage='40' :show-border='false' :size='100' :circle-size='24'/>

jsoncode
4k 声望786 粉丝

I'm jsoncode