条纹圆形进度条(彩虹色)

实现出来的效果

可自定义颜色区间完整代码参考下面(vue2)

javascript 复制代码
<template>
  <div class="rainbow-progress-container">
    <svg :width="size" :height="size" viewBox="0 0 240 240">
      <!-- 底层背景环 -->
      <circle
        cx="120"
        cy="120"
        r="100"
        fill="none"
        stroke="#334155"
        stroke-opacity="0.3"
        stroke-width="24"
        :stroke-dasharray="bgDashArray"
        stroke-linecap="butt"
      ></circle>

      <!-- 彩虹色条纹环 - 每个条纹根据角度计算颜色 -->
      <g id="rainbow-ring">
        <circle
          v-for="(block, idx) in coloredBlocks"
          :key="idx"
          cx="120"
          cy="120"
          r="100"
          fill="none"
          :stroke="block.color"
          stroke-width="24"
          :stroke-dasharray="block.dashArray"
          :stroke-dashoffset="block.dashOffset"
          stroke-linecap="butt"
          transform="rotate(-90 120 120)"
        ></circle>
      </g>
    </svg>

    <div class="text-center">
      <div class="percentage">{{ percentage }}%</div>
      <h2>已完成</h2>
    </div>
  </div>
</template>

<script>
export default {
  name: "RainbowProgress",
  props: {
    percentage: {
      type: Number,
      default: 70,
      validator: (val) => val >= 0 && val <= 100,
    },
    size: {
      type: Number,
      default: 96,
    },
    radius: {
      type: Number,
      default: 100,
    },
    blockCount: {
      type: Number,
      default: 40,
    },
    gapSize: {
      type: Number,
      default: 6,
    },
  },
  computed: {
    circumference() {
      return 2 * Math.PI * this.radius;
    },
    blockLength() {
      return (
        (this.circumference - this.blockCount * this.gapSize) / this.blockCount
      );
    },
    bgDashArray() {
      return `${this.blockLength} ${this.gapSize}`;
    },
    rainbowColors() {
      return [
        { pos: 0.0, color: [255, 59, 48] }, // 红 #ff3b30
        { pos: 0.15, color: [255, 149, 0] }, // 橙 #ff9500
        { pos: 0.3, color: [255, 204, 0] }, // 黄 #ffcc00
        { pos: 0.45, color: [76, 217, 100] }, // 绿 #4cd964
        { pos: 0.6, color: [0, 122, 255] }, // 蓝 #007aff
        { pos: 0.75, color: [88, 86, 214] }, // 靛 #5856d6
        { pos: 0.9, color: [175, 82, 222] }, // 紫 #af52de
        { pos: 1.0, color: [255, 59, 48] }, // 红 #ff3b30 (回到红色)
      ];
    },
    coloredBlocks() {
      const blocks = [];
      const totalLength = this.circumference;

      for (let i = 0; i < this.blockCount; i++) {
        const blockStart = i * (this.blockLength + this.gapSize);
        const blockCenter = (blockStart + this.blockLength / 2) / totalLength;
        const color = this.getColorAtPosition(blockCenter);

        const activeBlocksFloat = (this.percentage / 100) * this.blockCount;
        const isActive = i < Math.floor(activeBlocksFloat);
        const isPartial = i === Math.floor(activeBlocksFloat);
        const remainderRatio =
          activeBlocksFloat - Math.floor(activeBlocksFloat);
        const partialLen = this.blockLength * remainderRatio;

        let dashArray, dashOffset;

        if (isActive) {
          dashArray = `${this.blockLength} ${totalLength - this.blockLength}`;
          dashOffset = -blockStart;
        } else if (isPartial && partialLen > 0) {
          dashArray = `${partialLen} ${totalLength - partialLen}`;
          dashOffset = -blockStart;
        } else {
          dashArray = `0 ${totalLength}`;
          dashOffset = 0;
        }

        blocks.push({
          color:
            isActive || (isPartial && partialLen > 0) ? color : "transparent",
          dashArray: dashArray,
          dashOffset: dashOffset,
        });
      }
      return blocks;
    },
  },
  methods: {
    getColorAtPosition(pos) {
      const colors = this.rainbowColors;

      for (let i = 0; i < colors.length - 1; i++) {
        const start = colors[i];
        const end = colors[i + 1];

        if (pos >= start.pos && pos <= end.pos) {
          const ratio = (pos - start.pos) / (end.pos - start.pos);

          const r = Math.round(
            start.color[0] + (end.color[0] - start.color[0]) * ratio,
          );
          const g = Math.round(
            start.color[1] + (end.color[1] - start.color[1]) * ratio,
          );
          const b = Math.round(
            start.color[2] + (end.color[2] - start.color[2]) * ratio,
          );

          return `rgb(${r}, ${g}, ${b})`;
        }
      }

      return "rgb(255, 59, 48)";
    },
  },
};
</script>

<style scoped>
.rainbow-progress-container {
  text-align: center;
  position: relative;
  display: inline-block;
  padding-top: 5px;
}

.text-center {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100%;
}

.percentage {
  font-weight: bold;
  font-size: 18px;
  color: #00509a;
}

h2 {
  margin: 0;
  font-size: 14px;
  color: #64748b;
  font-weight: normal;
  letter-spacing: 1px;
}
</style>
相关推荐
阿旭超级学得完17 小时前
Linux基础指令 四(apt,vim,git,cgdb)
linux·服务器·开发语言·数据结构·c++·git·vim
Vallelonga17 小时前
Rust 中的枚举
开发语言·rust
兰令水17 小时前
leecodecode【状态机DP】【2026.6.9打卡-java版本】
java·开发语言·算法
宸津-代码粉碎机17 小时前
Spring AI企业级实战|Agent长期记忆持久化落地,彻底解决多轮对话上下文丢失问题
java·开发语言·人工智能·后端·python·spring
在放️17 小时前
Python 爬虫 · bs4 模块基础
开发语言·爬虫·python
belong_my_offer17 小时前
Python 数据采集完全指南 —— 从零开始掌握网络爬虫与文件读取
开发语言·爬虫·python
Adorable老犀牛17 小时前
Prometheus 常用告警规则 rules.yml
开发语言·prometheus·exporter·nodeexpoeter
阿里matlab建模师17 小时前
【机场停机位分配】matlab实现基于遗传算法的机场停机位分配优化研究
开发语言·算法·数学建模·matlab·全国大学生数学建模竞赛
xiaoshuaishuai817 小时前
C# Avalonia 依赖属性与WPF的区别
开发语言·c#·wpf