微信小程序实现动态环形进度条组件

微信小程序实现动态环形进度条组件

引言

在小程序开发中,环形进度条是一种常见的UI组件,常用于展示任务完成度、加载状态等场景。本文将介绍如何使用微信小程序原生能力实现一个功能完整、交互友好的环形进度条组件。

组件效果展示

该环形进度条组件具有以下特点:

  • 支持自定义进度值(0%-100%)
  • 平滑的进度过渡效果

核心实现原理

1. 布局结构设计

首先,让我们来看一下组件的WXML结构:

xml 复制代码
<view style="height: 200rpx;"></view>
<view style="height: 250rpx;overflow: hidden;">
  <view class="progress-circle" id="test" style="background: {{progressStyle}}">
    <view class="mask"></view>
  </view>
  <!-- <image 
    class="progress-icon" 
    src="you url" 
    style="{{iconStyle}}"
  ></image> -->
</view>
<button bindtap="setProgress" data-percent="0">设为0</button>
<button bindtap="setProgress" data-percent="12.5">设为12.5%</button>
<button bindtap="setProgress" data-percent="25">设为25%</button>
<button bindtap="setProgress" data-percent="37.5">设为37.5%</button>
<button bindtap="setProgress" data-percent="50">设为50%</button>

结构分析

  • 最外层使用一个view作为容器,设置高度为250rpx并隐藏溢出内容
  • 内部的progress-circle是实现环形进度条的核心元素
  • mask用于遮挡内部区域,形成环形效果
  • 注释掉的image标签用于在进度条末端显示图标(可根据需要启用)
  • 下方的按钮用于测试不同进度值的显示效果

2. 样式实现

相关的WXSS样式:

css 复制代码
.progress-circle {
  width: 500rpx;
  height: 500rpx;
  border-radius: 50%;
  position: relative;
}

.mask {
  position: absolute;
  width: 460rpx;     /* 500 - 20*2 */
  height: 460rpx;
  background: #fff;
  border-radius: 50%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%)
}

样式分析

  • progress-circle设置为500rpx的正方形,并通过border-radius: 50%变成圆形
  • mask是一个460rpx的圆形,通过绝对定位和transform居中,形成环形的"空心"效果

3. 核心逻辑实现

以下部分是JS逻辑,用于计算和更新进度条样式:

javascript 复制代码
Page({
  data: {
    progressStyle: 'conic-gradient(from -90deg,#007bff 0% 0%, #ccc 0% 100%)'
  },

  setProgress(e) {
    const percent = e.currentTarget.dataset.percent;
    const rotationAngle = -90 + (percent / 100) * 360;

    // 计算图标位置(极坐标转直角坐标)
    const radius = 250; // 进度条半径(rpx)
    const angle = (-90 + (percent / 100) * 360) * (Math.PI / 180); // 转弧度
    const x = radius * Math.cos(angle); // X轴偏移
    const y = radius * Math.sin(angle); // Y轴偏移
  
    this.setData({
        progressStyle: `conic-gradient(from -90deg, #007bff 0% ${percent}%, #ccc ${percent}% 100%)`, // 这里设置了图标位置
        iconStyle: `
          position: absolute;
          width: 40rpx;
          height: 40rpx;
          transform: rotate(${rotationAngle}deg);
          transform-origin: center 250rpx;
          left: 50%;
          top: 0;
          margin-left: -20rpx;
          transition: transform 0.3s ease;
        `
    });
  }
})

逻辑分析

  • 使用conic-gradient(锥形渐变)实现环形进度效果
  • 从-90度开始计算,对应时钟12点方向
  • 根据传入的百分比计算进度条的结束位置
  • 同时计算图标的位置,使用极坐标转直角坐标的方法

技术要点解析

1. 锥形渐变实现环形进度

核心技术点是使用CSS的conic-gradient属性:

javascript 复制代码
progressStyle: `conic-gradient(from -90deg, #007bff 0% ${percent}%, #ccc ${percent}% 100%)`
  • from -90deg:设置渐变的起始角度为-90度(即时钟12点方向)
  • #007bff 0% ${percent}%:从0%到指定百分比使用蓝色
  • #ccc ${percent}% 100%:从指定百分比到100%使用灰色

这种实现方式相比传统的SVG或Canvas方案,代码更简洁,性能也更好。更加推荐在C端应用小程序中使用

2. 图标位置计算

根据需求所需:

javascript 复制代码
// 计算图标位置(极坐标转直角坐标)
const radius = 250; // 进度条半径(rpx)
const angle = (-90 + (percent / 100) * 360) * (Math.PI / 180); // 转弧度
const x = radius * Math.cos(angle); // X轴偏移
const y = radius * Math.sin(angle); // Y轴偏移

这段代码使用了三角函数将极坐标转换为直角坐标,从而计算出图标在环形进度条上的精确位置。

3. 响应式交互设计

通过按钮绑定setProgress方法,实现了点击按钮更新进度的交互(用于测试)最终实现需要结合业务需求动态计算:

xml 复制代码
<button bindtap="setProgress" data-percent="0">设为0</button>
<button bindtap="setProgress" data-percent="12.5">设为12.5%</button>
<button bindtap="setProgress" data-percent="25">设为25%</button>
<button bindtap="setProgress" data-percent="37.5">设为37.5%</button>
<button bindtap="setProgress" data-percent="50">设为50%</button>

每个按钮都通过data-percent属性传递不同的进度值,点击时触发setProgress方法更新进度条。 以下粘贴我在具体业务场景中实现的动态计算:

javascript 复制代码
 calculatePercent(totalGrowth) {
    const { levelList } = this.data;
    const percentRanges = [0, 12.5, 25, 37.5, 50];
    let currentLevel = 0;
    for (let i = 0; i < levelList.length; i++) {
      if (totalGrowth >= levelList[i].growMin && totalGrowth <= levelList[i].growMax) {
        currentLevel = i;
        break;
      }
    }
    const { growMin, growMax } = levelList[currentLevel];
    const minPercent = percentRanges[currentLevel];
    const maxPercent = currentLevel < percentRanges.length - 1 ? percentRanges[currentLevel + 1] : 100;
  
    const percent = minPercent + ((totalGrowth - growMin) / (growMax - growMin)) * (maxPercent - minPercent);
    const roundedPercent = Math.floor(percent * 10) / 10;
  
    return Math.min(roundedPercent, maxPercent);
  },

优化建议

  1. 添加动画过渡效果 : 可以在CSS中为progress-circle添加transition属性,使进度变化更加平滑:

    css 复制代码
    .progress-circle {
      transition: background 0.3s ease;
    }
  2. 封装为自定义组件: 将环形进度条封装为独立的自定义组件,提高复用性:

    javascript 复制代码
    // components/progress-ring/progress-ring.js
    Component({
      properties: {
        percent: {
          type: Number,
          value: 0
        },
        color: {
          type: String,
          value: '#007bff'
        },
        backgroundColor: {
          type: String,
          value: '#ccc'
        }
      },
      data: {
        progressStyle: ''
      },
      observers: {
        'percent': function(percent) {
          this.updateProgress(percent);
        }
      },
      methods: {
        updateProgress(percent) {
          this.setData({
            progressStyle: `conic-gradient(from -90deg, ${this.data.color} 0% ${percent}%, ${this.data.backgroundColor} ${percent}% 100%)`
          });
        }
      }
    })
  3. 添加更多配置项: 可以扩展组件,添加更多可配置项,如:

    • 圆环宽度
    • 动画时长
    • 是否显示文字百分比
    • 是否启用图标
  4. 性能优化 : 对于频繁更新的场景,可以使用requestAnimationFrame或小程序的animation API来提高性能。

完整使用示例

以下是一个完整的使用示例,展示如何在页面中集成和使用这个环形进度条组件:

xml 复制代码
<!-- 页面WXML -->
<view class="container">
  <text class="title">任务完成度</text>
  <view class="progress-container">
    <view class="progress-circle" style="background: {{progressStyle}}">
      <view class="mask">
        <text class="percent-text">{{currentPercent}}%</text>
      </view>
    </view>
  </view>
  <view class="controls">
    <button bindtap="setProgress" data-percent="0">重置</button>
    <button bindtap="setProgress" data-percent="25">25%</button>
    <button bindtap="setProgress" data-percent="50">50%</button>
    <button bindtap="setProgress" data-percent="75">75%</button>
    <button bindtap="setProgress" data-percent="100">完成</button>
  </view>
</view>
css 复制代码
/* 页面WXSS */
.container {
  padding: 40rpx;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.title {
  font-size: 36rpx;
  font-weight: bold;
  margin-bottom: 60rpx;
}

.progress-container {
  width: 500rpx;
  height: 500rpx;
  margin-bottom: 60rpx;
  display: flex;
  justify-content: center;
  align-items: center;
}

.progress-circle {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  position: relative;
  transition: background 0.3s ease;
}

.mask {
  position: absolute;
  width: 88%;
  height: 88%;
  background: #fff;
  border-radius: 50%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  justify-content: center;
  align-items: center;
}

.percent-text {
  font-size: 48rpx;
  font-weight: bold;
  color: #007bff;
}

.controls {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 20rpx;
  width: 100%;
}

.controls button {
  flex: 1;
  min-width: 120rpx;
}
javascript 复制代码
// 页面JS
Page({
  data: {
    progressStyle: 'conic-gradient(from -90deg, #007bff 0% 0%, #ccc 0% 100%)',
    currentPercent: 0
  },

  setProgress(e) {
    const percent = e.currentTarget.dataset.percent;
    this.setData({
      progressStyle: `conic-gradient(from -90deg, #007bff 0% ${percent}%, #ccc ${percent}% 100%)`,
      currentPercent: percent
    });
  }
})

总结

本文介绍了如何在微信小程序中实现一个动态环形进度条组件,主要使用了以下技术:

  1. CSS锥形渐变 :使用conic-gradient实现环形进度效果
  2. 绝对定位和transform:实现环形的"空心"效果和元素居中
  3. 数据绑定 :通过setData动态更新进度条样式
  4. 事件处理:通过按钮点击事件更新进度值

这个实现方案具有以下优点:

  • 代码简洁:使用CSS渐变实现,无需复杂的Canvas或SVG代码
  • 性能良好:样式更新通过数据绑定实现,性能稳定
  • 易于扩展:可以方便地添加动画效果、自定义颜色等功能
  • 兼容性好:使用的CSS属性在现代浏览器和小程序中都有良好支持

通过本文的学习,你应该能够理解环形进度条的实现原理,并在自己的小程序项目中灵活应用和扩展这个组件。

写在最后

环形进度条是一种视觉效果出色的UI组件,合理使用可以提升应用的用户体验。希望本文的实现方案对你有所帮助,如果你有更好的想法或建议,欢迎在评论区分享交流。

如果你觉得本文有用,记得点赞、收藏和关注哦!

相关推荐
小居6732 小时前
Elpis 一个能够自定义的nodejs前后端框架
前端
特别橙的橙汁2 小时前
Node.js 调用可执行文件时的 stdout 缓冲区问题
前端·node.js·swift
yiranlater2 小时前
点云八叉树处理
前端
榴莲CC2 小时前
VK1620 抗噪数显LED驱动芯片数码管显示IC内置 RC振荡器/8级整体亮度可调
前端
@Autowire2 小时前
Layout-box-sizing是 CSS 中控制元素盒模型计算方式的核心属性,直接决定了元素的 width/height 是否包含内边距和边框
前端
alamhubb2 小时前
反感pnpm的全链路污染?可以了解下这个对原项目零侵入,零修改完全兼容npm的monorepo工具
前端·javascript·node.js
叁两2 小时前
“死了么”用户数翻800倍,估值近1亿,那我来做个“活着呢”!
前端·人工智能·产品
AdleyTales3 小时前
vscode识别不了@提示找不到路径解决
前端·javascript·vscode
去哪儿技术沙龙3 小时前
去哪儿网前端代码自动生成技术实践
前端·ai编程