component-svg圆环进度百分比图(顶部文本,中间图形,底部文本)

svg图形展示

vue3 + ts

图表顶部自定义编辑文本,圆环可自定义背景与颜色,底部为图表文本。

在ant 或echarts中也有类似的圆环进度

父组件调用

顶部自定义为文本描述信息

对应进度可计算为百分比,圆环宽度,进度以及背景颜色,百分比字体颜色,百分比底部文本,以及进度圆环开始渲染位置。

javascript 复制代码
<template>
  <div class="content-all">
    <div class="content-top-text">echarts文本描述</div>
    <!-- echarts整体-(图表+底部文本)  -->
    <div class="echarts-all">
       <!-- echarts整体-图表 -->
      <div class="echarts-chart">
        <HDemoSon
          :progress="75"
          :strokeWidth="12"
          :progressColor="'#4CAF50'"
          :backgroundColor="'#FFF'"
          :percentageTextColor="'#4CAF50'"
          :title="'完成率'"
          direction="top"
        />
      </div>
      <!-- echarts整体-底部文本 -->
      <div class="echarts-bottom-text">累计</div>  
    </div>
  </div>
</template>

<script setup>
import HDemoSon from './HDemoSon.vue'
</script>

<style scoped lang="scss">

@function vh($px) {
  @return calc($px / 1080) * 100vh;
}
// 整体样式
.content-all {
  padding: 2px;
  height: 100%;
  width: 100%;
  background-color: #0C6299;
}

// 顶部数据统计
.content-top-text{
  width: 100%;
  height: 20px;
  background-color: blueviolet;
}

// echarts整体-(图表+底部文本)
.echarts-all {
  width: 70px; 
  height: 90px;
  background-color: red;
}

// echarts整体-图表
.echarts-chart {
  width: 70px;
  height: 70px;
}


// echarts整体-底部文本
.echarts-bottom-text {
  background-color: aqua;
  height: 20px;
  width: 100%;
  line-height: 20px;
  text-align: center;
  font-weight: 500;
  font-size: 10px;
}
</style>

子组件-进度svg图形

图表正常接收数据进行渲染,底部自定义文本信息或样式

javascript 复制代码
<!-- 进度圆环组件 -->
<template>
  <div class="progress-ring">
    <!-- SVG -->
    <svg viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
      <!-- 背景圆环 -->
      <circle
        :stroke="backgroundColor"
        :stroke-width="strokeWidth"
        fill="transparent"
        :r="radius"
        cx="50"
        cy="50"
      />
      <!-- 进度圆环 -->
      <circle
        :stroke="progressColor"
        :stroke-width="strokeWidth"
        fill="transparent"
        :r="radius"
        cx="50"
        cy="50"
        :stroke-dasharray="circumference"
        :stroke-dashoffset="strokeDashoffset"
        :transform="rotationTransform"
      />
    </svg>
    <!-- 文本内容 -->
    <div class="progress-ring-text-container">
      <div class="progress-ring-percentage" :style="percentageStyle">
        {{ percentageText }}
      </div>
       <div v-if="title" class="progress-ring-title">
        {{ title }}
      </div>
    </div>
  </div>
</template>

<script setup>
import { computed } from 'vue'

const props = defineProps({
  progress: {
    type: Number,
    default: 0,
    validator: (v) => v >= 0 && v <= 100
  },
  // 圆环宽度   
  strokeWidth: {
    type: Number,
  },
  // 进度圆环背景颜色  
  progressColor: {
    type: String,
  },
  // 进度圆环背景颜色   
  backgroundColor: {
    type: String,
  },
  // 字体   
  title: {
    type: String,
    default: ''
  },
  // 百分比字体颜色
  percentageTextColor: {
    type: String,
  },
  // 进度圆环开始绘制方向  
  direction: {
    type: String,
    default: 'top',
    validator: (v) => ['top', 'right', 'bottom', 'left'].includes(v)
  }
})

// 半径基于 viewBox 100x100
const radius = computed(() => 50 - props.strokeWidth / 2)
const circumference = computed(() => 2 * Math.PI * radius.value)

const strokeDashoffset = computed(() => {
  const p = Math.max(0, Math.min(100, props.progress))
  return circumference.value - (p / 100) * circumference.value
})

const rotationTransform = computed(() => {
  const map = { top: -90, right: 0, bottom: 90, left: 180 }
  return `rotate(${map[props.direction]} 50 50)`
})

// 百分比颜色
const percentageStyle = computed(() => ({
  color: props.percentageTextColor,
}))

// 百分比计算
const percentageText = computed(() => `${Math.round(props.progress)}%`)
</script>

<style scoped lang="scss">

@function vh($px) {
  @return calc($px / 1080) * 100vh;
}
/* 整体样式 */
.progress-ring {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.progress-ring svg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

/* 中心文本内容 */
.progress-ring-text-container {
  position: relative;
  text-align: center;
  font-family: Arial, sans-serif;
  line-height: 1.2;
  z-index: 1;
}

/* 百分比 */
.progress-ring-percentage{
    font-size: 16px;
}

/* 文本 */
.progress-ring-title{
    font-size: 12px;
    color: #666;
}
</style>
相关推荐
不可求~1 天前
多模型路由怎么落地:把任务分流写进项目的最小实现
java·前端·数据库
AC赳赳老秦1 天前
公开图片 OCR 数据提取:OpenClaw 合规采集公开信息图,识别文字与表格并转化为结构化数据
java·大数据·前端·数据库·python·php·openclaw
谢慧琼1 天前
2026零基础做企业网站,低成本搭建官方网站
服务器·前端·javascript
weixin_431600441 天前
前端数据埋点(1):一次点击如何变成一条埋点
前端·js·数据埋点
IT_陈寒1 天前
Python线程池吞了异常还不告诉我,这谁顶得住啊
前端·人工智能·后端
计算机魔术师1 天前
同样是AI辅助建造,为什么有的游戏三个月就烂尾了?
前端
用户921080262861 天前
0. 做 Agent 产品,前端 AI 组件框架怎么选?
前端
拾年2751 天前
React Hooks 进阶篇:useMemo / useCallback / useReducer / useImperativeHandle,用「算账 +
前端·javascript·react.js
Jackson__1 天前
面试官:如何在老项目中使用新框架,并实现通信?
前端·javascript·面试
拾年2751 天前
React Hooks 保姆级教程:把组件想象成失忆的打工人,4 个 Hook 带你原地起飞
前端·javascript·react.js