uniapp 用css实现圆形进度条组件

247 阅读3分钟

一个基于 UniApp + Vue2 的圆形进度条组件,支持自定义进度、颜色、文本和内圈样式。

效果预览

image.png

功能特性

  • 圆形进度条显示
  • 完全可自定义的颜色和样式
  • 支持数值和描述文本
  • 灵活的内圈配置
  • 响应式设计,支持 rpx 单位
  • 性能比canvas好太多

实现代码

MDPieProgress.vue 组件实现代码:

<template>
  <view class="pie-progress">
    <view class="progress-container" :style="{ width: config.width + 'rpx', height: config.height + 'rpx' }">
      <!-- 使用相同的计算方式 -->
      <view class="progress-bg" :style="{
        background: `conic-gradient(${config.progress.color} 0, ${config.progress.color} ${config.value}%, ${config.progress.backgroundColor } ${config.value}%, ${config.progress.backgroundColor} 100%)`,
        '-webkit-mask': `radial-gradient(transparent 0%, transparent ${config.inner.width * 70}%, #000 ${config.inner.width * 70}%, #000 0)`,
        width: config.width + 'rpx',
        height: config.height + 'rpx'
      }"></view>

      <!-- 内圈背景 -->
      <view
        class="inner-background"
        :style="{
          width: (config.inner.width * 100) + '%',
          height: (config.inner.width * 100) + '%',
          backgroundColor: config.inner.backgroundColor
        }"
      ></view>

      <!-- 文字内容 -->
      <view class="progress-inner flex-c-c">
        <view class="progress-text flex-c">
          <text
            class="value"
            v-if="config.valueText && config.valueText.value"
            :style="{
              color: config.valueText.color,
              fontSize: config.valueText.fontSize,
              fontWeight: config.valueText.fontWeight
            }"
          >{{ config.valueText.value }}</text>
          <text
            class="desc"
            v-if="config.descText && config.descText.value"
            :style="{
              color: config.descText.color,
              fontSize: config.descText.fontSize,
              fontWeight: config.descText.fontWeight
            }"
          >{{ config.descText.value }}</text>
        </view>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  props: {
    // 配置对象
    opts: {
      type: Object,
      default: () => ({})
    }
  },
  computed: {
    config() {
      return this.mergeWithDefault(this.opts)
    }
  },
  data() {
    return {
      defaultConfig: {
        // 进度值 (0-100)
        value: 0,
        width: 200, // 整体宽度 (单位: rpx)
        height: 200, // 整体高度 (单位: rpx)
        // 进度条配置
        progress: {
          color: '#0E9BF3', // 进度条颜色
          backgroundColor: '#E0E4EB' // 进度条背景色
        },
        // 值文本
        valueText: {
          value: "",
          color: "#3B3B4D",
          fontSize: "28rpx",
          fontWeight: "bold",
        },
        // 描述文本
        descText: {
          value: "",
          color: "#999",
          fontSize: "20rpx",
          fontWeight: "normal"
        },
        // 内圈配置
        inner: {
          width: 0.8, // 内圈宽度占比 (0-1) 占比越大内圈越大,进度条越细
          backgroundColor: "transparent" // 内圈背景色
        }
      }
    }
  },
  methods: {
    // 合并函数
    mergeWithDefault(opts) {
      if (!opts || typeof opts !== 'object') {
        return { ...this.defaultConfig };
      }
      const result = {
        // 第一层合并
        ...this.defaultConfig,
        ...opts,
        // 第二层深度合并
        progress: {
          ...this.defaultConfig.progress,
          ...(opts.progress || {})
        },
        valueText: {
          ...this.defaultConfig.valueText,
          ...(opts.valueText || {})
        },
        descText: {
          ...this.defaultConfig.descText,
          ...(opts.descText || {})
        },
        inner: {
          ...this.defaultConfig.inner,
          ...(opts.inner || {})
        }
      }
      return result
    }
  }
}
</script>

<style lang="scss">
.pie-progress {
  .progress-container {
    position: relative;

    .progress-bg {
      position: absolute;
      top: 0;
      left: 0;
      border-radius: 50%;
    }

    .inner-background {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      border-radius: 50%;
      z-index: 1;
    }

    .progress-inner {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      z-index: 2;
      .progress-text {
        flex-direction: column;
        flex-shrink: 0;
        .value {
          font-weight: bold;
        }
      }
    }
  }
}
</style>

父组件使用案例代码:

<template>
  <view>
    ......
    <MDPieProgress :opts="getPieProgressConfig(item)"></MDPieProgress>
  </view>
</template>

<script>
export default {
  methods: {
    getPieProgressConfig(item) {
      return {
        value:item.rate,
        width: 108,
        height: 108,
        progress: {
          color: isSub ? '#FF451C' : '#0E9BF3',
          backgroundColor: '#E0E4EB'
        },
        valueText: {
          value: `${item.rate}%`,
          color: '#3B3B4D',
          fontSize: '28rpx',
          fontWeight: 'bold'
        },
        descText: {
          value: '达成率',
          color: '#999999',
          fontSize: '20rpx',
          fontWeight: 'normal'
        },
        inner: {
          width: 0.8,
          backgroundColor: '#FFFFFF'
        }
      }
    }, 
  }
}
</script>

操作手册(配置参数)

基础配置

参数类型默认值必填说明示例
valueNumber0进度值,范围 0-10075
widthNumber200组件整体宽度,单位 rpx250
heightNumber200组件整体高度,单位 rpx250

进度条配置 progress

参数类型默认值必填说明示例
progress.colorString'#0E9BF3'已完成进度部分颜色'#42b983'
progress.backgroundColorString'#E0E4EB'未完成进度部分颜色'#f0f9eb'

数值文本配置 valueText

参数类型默认值必填说明示例
valueText.valueString""显示的数值文本内容"75%"
valueText.colorString"#3B3B4D"数值文字颜色"#ff5722"
valueText.fontSizeString"28rpx"数值文字大小"32rpx"
valueText.fontWeightString"bold"数值文字粗细"600"

描述文本配置 descText

参数类型默认值必填说明示例
descText.valueString""描述文本内容"完成率"
descText.colorString"#999"描述文字颜色"#666"
descText.fontSizeString"20rpx"描述文字大小"24rpx"
descText.fontWeightString"normal"描述文字粗细"normal"

内圈配置 inner

参数类型默认值必填说明示例
inner.widthNumber0.8内圈宽度占比 (0-1),值越大内圈越大,进度条越细0.7
inner.backgroundColorString"transparent"内圈背景颜色"#FFFFFF"

注意事项

  1. 单位系统:所有尺寸单位使用 rpx,确保在不同设备上的适配
  2. 渐进式配置:只需提供需要覆盖的配置项,未提供的项将使用默认值
  3. 内圈宽度inner.width 的值范围是 0-1,值越大内圈越大,进度条越细
  4. 背景色透明:设置 inner.backgroundColor: "transparent" 可使内圈透明