CSS样式 加载展样式展示百分百

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

组件使用

<CircleProgress
  :value="50"
   color="#444999"
   :size="40"
 />

组件代码

<template>
  <div class="circle-progress-wrapper">
    <div class="circle-progress" :style="{ width: size + 'px', height: size + 'px' }">
      <svg :width="size" :height="size" class="circle-svg">
        <!-- 绘制10个正梯形 -->
        <g v-for="(dot, index) in dots" :key="index">
          <defs v-if="dot.clipPercent < 1">
            <clipPath :id="`clip-${index}`">
              <rect 
                :x="dot.x - dotTopWidth / 2" 
                :y="dot.y - dotHeight / 2" 
                :width="dotTopWidth * dot.clipPercent" 
                :height="dotHeight"
              />
            </clipPath>
          </defs>
          <polygon
            :points="getTrapezoidPoints(dot.x, dot.y)"
            :fill="dot.fillColor"
            :transform="`rotate(${dot.angle} ${dot.x} ${dot.y})`"
            :clip-path="dot.clipPercent < 1 ? `url(#clip-${index})` : undefined"
            class="dot"
          />
        </g>
      </svg>
    </div>
    <!-- 百分比文字在右侧 -->
    <span class="percentage-text" :style="{ color: textColor, fontSize: textSize + 'px' }">
      {{ value }}%
    </span>
  </div>
</template>

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

interface Props {
  value: number // 百分比值 0-100
  color?: string // 激活点的颜色
  backgroundColor?: string // 未激活点的颜色
  textColor?: string // 文字颜色
  size?: number // 组件大小
  dotCount?: number // 总点数
}

const props = withDefaults(defineProps<Props>(), {
  value: 0,
  color: '#3b82f6',
  backgroundColor: '#e5e7eb',
  textColor: '#1f2937',
  size: 120,
  dotCount: 10
})

// 计算属性
const center = computed(() => props.size / 2)
const radius = computed(() => props.size * 0.32) // 调整半径让圆圈更紧凑

// 正梯形的尺寸(上宽下窄)
const dotTopWidth = computed(() => props.size * 0.12) // 上边宽度
const dotHeight = computed(() => props.size * 0.12) // 高度
const dotBottomWidth = computed(() => props.size * 0.08) // 下边宽度

// 生成正梯形的点坐标
const getTrapezoidPoints = (centerX: number, centerY: number) => {
  const topWidth = dotTopWidth.value
  const bottomWidth = dotBottomWidth.value
  const height = dotHeight.value
  
  // 正梯形的四个点(上宽下窄)
  const points = [
    [centerX - topWidth / 2, centerY - height / 2], // 左上
    [centerX + topWidth / 2, centerY - height / 2], // 右上
    [centerX + bottomWidth / 2, centerY + height / 2], // 右下
    [centerX - bottomWidth / 2, centerY + height / 2], // 左下
  ]
  
  return points.map(p => `${p[0]},${p[1]}`).join(' ')
}

// 根据百分比计算应该激活多少个点(支持小数)
const activeDots = computed(() => {
  return (props.value / 100) * props.dotCount
})

// 计算每个点的位置和激活状态
const dots = computed(() => {
  const result = []
  const angleStep = 360 / props.dotCount
  const activeCount = activeDots.value
  
  for (let i = 0; i < props.dotCount; i++) {
    const angle = i * angleStep - 90 // -90度让第一个点在顶部
    const radian = (angle * Math.PI) / 180
    const x = center.value + radius.value * Math.cos(radian)
    const y = center.value + radius.value * Math.sin(radian)
    
    // 计算当前点的激活状态
    let fillColor = 'transparent' // 未激活的点使用透明色,不显示
    let clipPercent = 1 // 默认显示完整的梯形
    
    if (i < Math.floor(activeCount)) {
      // 完全激活的点
      fillColor = props.color
      clipPercent = 1
    } else if (i === Math.floor(activeCount) && activeCount % 1 !== 0) {
      // 部分激活的点(半点)- 裁剪显示部分梯形
      fillColor = props.color
      clipPercent = activeCount % 1
    }
    
    result.push({
      x,
      y,
      angle: angle + 90, // 让梯形指向圆心
      fillColor,
      clipPercent
    })
  }
  
  return result
})

// 文字大小
const textSize = computed(() => {
  return Math.max(props.size * 0.25, 18)
})
</script>

<style scoped lang="scss">
.circle-progress-wrapper {
  display: inline-flex;
  align-items: center;
  gap: 16px;
}

.circle-progress {
  position: relative;
  display: inline-block;
}

.circle-svg {
  display: block;
}

.dot {
  transition: fill 0.3s ease;
}

.percentage-text {
  font-weight: 600;
  line-height: 1;
}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值