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>
相关推荐
陪我去看海11 小时前
被要求猛出小程序,做了这个多平台多环境的部署替我承受压力
前端·微信小程序·抖音小程序
Frag0ut11 小时前
告别插件时代:HTML5 视频播放对比 Flash/Silverlight/ActiveX 的性能与技术优势解析
前端·html5·视频播放·flash·activex·流媒体技术·silverlight
用户8186180280012 小时前
MongoDB文档模型设计——从建模到索引
前端
無名路人12 小时前
小程序点餐页吸顶滚动之分类按需加载,上划切换
前端·vue.js·微信小程序
竹林81813 小时前
把神经网络塞进一个浏览器标签页:端侧视觉 AI 的工程真相
前端·浏览器
乘风gg13 小时前
别跟风 AI 副业,工程师最该学的是 AI Coding
前端·ai编程·claude
Frag0ut13 小时前
深度解析:Chrome 自启动与后台常驻服务的作用、关闭方法及利弊权衡
前端·chrome
Amos_Web13 小时前
Rspack 源码解析(十五):Loader Runner 与 JS Loader 桥接
前端·rust·前端框架
YIAN14 小时前
从 SSE 流式到结构化输出:LangChain 三大 OutputParser 与 ToolCall 方案全实战
前端·langchain·node.js
DsirNg14 小时前
响应式的真正输入:为组件重新定义可用空间
css·flexbox·grid·前端架构·响应式布局·设计系统·container queries