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

一、完整代码:

javascript 复制代码
<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 - 图标宽度/2
						iconY - this.iconSize, // 上顶点Y = 中心点Y - 图标高度/2
						this.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>

二、最终效果:

相关推荐
2501_915918412 小时前
HTTPS 端口号详解 443 端口作用、iOS 抓包方法、常见 HTTPS 抓包工具与网络调试实践
android·网络·ios·小程序·https·uni-app·iphone
小蒜学长4 小时前
基于springboot 校园餐厅预约点餐微信小程序的设计与实现(代码+数据库+LW)
数据库·spring boot·微信小程序
cookqq4 小时前
Cursor和Hbuilder用5分钟开发微信小程序
微信小程序·小程序·curosor
老华带你飞5 小时前
考研论坛平台|考研论坛小程序系统|基于java和微信小程序的考研论坛平台小程序设计与实现(源码+数据库+文档)
java·vue.js·spring boot·考研·小程序·毕设·考研论坛平台小程序
毕设源码-钟学长6 小时前
【开题答辩全过程】以 基于微信小程序的美发服务系统的设计与实现为例,包含答辩的问题和答案
微信小程序·小程序
canglingyue7 小时前
微信小程序罗盘功能开发指南
微信小程序·小程序
三脚猫的喵10 小时前
微信小程序中实现AI对话、生成3D图像并使用xr-frame演示
前端·javascript·ai作画·微信小程序
2501_9151063211 小时前
App Store 软件上架全流程详解,iOS 应用发布步骤、uni-app 打包上传与审核要点完整指南
android·ios·小程序·https·uni-app·iphone·webview
快起来搬砖了11 小时前
实现一个优雅的城市选择器组件 - Uniapp实战
开发语言·javascript·uni-app
数学分析分析什么?11 小时前
Uniapp中使用renderjs实现OpenLayers+天地图的展示与操作
uni-app·openlayers·天地图·renderjs