当前位置: 首页 > news >正文

Uniapp编写微信小程序,绘制动态圆环进度条

一、完整代码:

<template><view class="home"><view><!-- 标题tab --><view class="title">我是标题</view></view><scroll-view scroll-y style="height: calc(100vh - 360rpx)"><view class="chartBox"><view class="chartBox-progress"><canvas canvas-id="progressCanvas":style="{width: canvasSize +iconSize*2 + 'px', height: canvasSize +iconSize*2 + 'px'}"></canvas></view><view class="content"><view class="content-value"><view class="value">{{info.progress}}</view><view class="text">%</view></view><view class="content-tips"><view class="text">预计还需</view><view class="value">{{info.remainingTime}}分钟</view></view></view></view></scroll-view></view>
</template><script>export default {data() {return {ctx: null,iconImage: '/static/monitor/cg.png',canvasSize: 208,iconSize: 34,ringWidth: 9,imagePath: '',imageLoaded: false,info: {progress: 0, // 初始化进度totalTime: 60, // 总时间(分钟)remainingTime: 60, // 剩余时间}}},watch: { // 监听进度变化,重绘圆环progress() {this.drawProgress();}},components: {},onLoad() {this.initCanvas();this.loadImage();this.simulateCharging();},methods: {initCanvas() {// 获取Canvas上下文this.ctx = uni.createCanvasContext('progressCanvas', this);},loadImage() {// 检查是否为本地资源if (this.iconImage.startsWith('/') || this.iconImage.startsWith('./')) {// 本地资源直接使用this.imagePath = this.iconImage;this.imageLoaded = true;this.drawProgress();} else {// 网络资源需要先下载uni.getImageInfo({src: this.iconImage,success: (res) => {this.imagePath = res.path;this.imageLoaded = true;this.drawProgress();},fail: (err) => {console.error('获取图片信息失败:', err);this.drawProgress();}});}},drawProgress() {// 计算总画布尺寸(包含图标空间)const totalSize = this.canvasSize + this.iconSize * 2;// 计算真正的中心点const center = totalSize / 2;// 计算圆环半径const radius = (this.canvasSize - this.ringWidth) / 2;// 清空画布this.ctx.clearRect(0, 0, totalSize, totalSize+ this.iconSize);// 绘制背景圆环this.drawInnerShadow(this.ctx, center, radius, this.ringWidth);this.ctx.beginPath();this.ctx.arc(center, center - this.iconSize / 2, radius, 0, Math.PI * 2);this.ctx.setStrokeStyle('#ffd1bf');this.ctx.setLineWidth(this.ringWidth);this.ctx.stroke();// 绘制进度圆环this.ctx.beginPath();const startAngle = -Math.PI / 2; // 从12点位置开始const endAngle = startAngle + (this.info.progress / 100) * (Math.PI * 2);this.ctx.arc(center, center - this.iconSize / 2, radius, startAngle, endAngle);this.ctx.setStrokeStyle('#FF6D33');this.ctx.setLineWidth(this.ringWidth);this.ctx.stroke();// 计算图标位置const iconAngle = startAngle + (this.info.progress / 100) * (Math.PI * 2);const iconX = center + Math.cos(iconAngle) * radius;const iconY = center + Math.sin(iconAngle) * radius;this.ctx.save();if (this.imageLoaded) {// 绘制图标(修正坐标计算)this.ctx.drawImage(this.imagePath,iconX - this.iconSize / 2, // 左顶点X = 中心点X - 图标宽度/2iconY - this.iconSize, // 上顶点Y = 中心点Y - 图标高度/2this.iconSize,this.iconSize);} else {// 图标加载失败时显示默认图标this.ctx.beginPath();this.ctx.arc(iconX, iconY, this.iconSize / 2, 0, Math.PI * 2);this.ctx.setFillStyle('#FF6D33');this.ctx.fill();}this.ctx.restore();this.ctx.draw();// 更新剩余时间this.info.remainingTime = Math.round((100 - this.info.progress) * this.info.totalTime / 100);},drawInnerShadow(ctx, center, radius, ringWidth) {const shadowColor = 'rgba(255, 209, 191, 0.15)';const shadowWidth = 6;// 绘制外环阴影ctx.beginPath();ctx.arc(center, center- this.iconSize / 2, radius + ringWidth / 2 + shadowWidth / 2, 0, Math.PI * 2);ctx.setFillStyle(shadowColor);ctx.fill();// 绘制内环阴影(覆盖内部区域)ctx.beginPath();ctx.arc(center, center- this.iconSize / 2, radius - ringWidth / 2 - shadowWidth / 2, 0, Math.PI * 2);ctx.setFillStyle('#ffffff');ctx.fill();},onImageLoad() {// 图片加载成功this.imageLoaded = true;this.drawProgress();},// 模拟进度更新simulateCharging() {if (this.info.progress < 100) {setTimeout(() => {this.info.progress += 1;this.drawProgress();this.simulateCharging();}, 100);}}}}
</script><style scoped>.home {position: relative;width: 100vw;height: 100vh;background: #ecf0f1;}.home .chartBox {position: relative;left: 50%;top: 128rpx;transform: translateX(-50%);width: 480rpx;height: 480rpx;background: linear-gradient(180deg, #FFFFFF 0%, #fff8f6 100%);box-shadow: 0rpx 20rpx 20rpx 0rpx rgba(255, 209, 191, 0.1);border-radius: 100%;}.home .chartBox-progress {z-index: 2;position: relative;overflow: visible !important;display: flex;flex-direction: column;align-items: center;justify-content: center;height: 480rpx;}.home .content {z-index: 9;position: absolute;top: 144rpx;left: 50%;transform: translateX(-50%);display: flex;flex-direction: column;align-items: center;}.home .content-value {display: flex;align-items: baseline;flex-wrap: nowrap;}.home .content-value .value {font-weight: 500;font-size: 120rpx;color: #ff8859;}.home .content-value .text {margin-bottom: 8rpx;font-weight: 400;font-size: 32rpx;color: #ffded1;}.home .content-tips {margin: 0 0 20rpx;}.home .content-tips .text {font-weight: 400;font-size: 24rpx;color: #3F5680;}.home .content-tips .value {margin: 0 8rpx;font-weight: 600;font-size: 24rpx;color: #ff8859;}</style>

二、最终效果:

在这里插入图片描述

http://www.xdnf.cn/news/1184689.html

相关文章:

  • LIMA:大语言模型对齐的“少即是多”革命——原理、实验与范式重构
  • 软件工程:软件需求
  • 图书推荐-由浅入深的大模型构建《从零构建大模型》
  • 【模型剪枝1】结构化剪枝论文学习笔记
  • k8s-MongoDB 副本集部署
  • XORIndex:朝鲜不断发展的供应链恶意软件再次瞄准 npm 生态系统
  • Kubernetes配置管理
  • Axios基本使用
  • GUI界面已经移植完,添加欠缺字,微调GUI界面说明
  • Kafka运维实战 15 - kafka 重设消费者组位移入门和实战【实战】
  • 时间和空间复杂度
  • 八股文之JVM
  • DNS 服务正反向解析与 Web 集成实战:从配置到验证全流程
  • Day 21: 常见的降维算法
  • 专题:2025电商增长新势力洞察报告:区域裂变、平台垄断与银发平权|附260+报告PDF、原数据表汇总下载
  • 小米8(dipper)刷入kernelSU内核root定制rom系统教程以及安装LSPosed模块
  • Windows-WSL-Docker端口开放
  • FunASR实时多人对话语音识别、分析、端点检测
  • NLP验证自动化脚本优化
  • 从热点到刚需:SmartMediaKit为何聚焦B端视频系统建设?
  • 【lucene】AttributeSource概述
  • Ethereum:Geth + Clef 本地开发环境,如何优雅地签名并发送一笔以太坊交易?
  • Linux 内存深度剖析:栈与堆的底层机制与实战指南
  • 汽车免拆诊断案例 | 2010款奔驰E200 CGI车EPS OFF灯异常点亮
  • MCP 与传统集成方案深度对决:REST API、GraphQL、gRPC 全方位技术解析
  • Linux725 磁盘阵列RAID0 RAID1
  • Linux库——库的制作和原理(1)_回顾动静态库、制作使用库
  • docker-compose:未找到命令的检查步骤和修复
  • 从数据孤岛到融合共生:KES V9 2025 构建 AI 时代数据基础设施
  • 65.第二阶段x64游戏实战-替换游戏lua打印可接任务