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>

二、最终效果:

相关推荐
陈不知代码1 小时前
uniapp创建vue3+ts+pinia+sass项目
前端·uni-app·sass
说私域1 小时前
公域流量向私域流量转化策略研究——基于开源AI智能客服、AI智能名片与S2B2C商城小程序的融合应用
人工智能·小程序
半生过往1 小时前
微信小程序文件下载与预览功能实现详解
微信小程序·小程序·notepad++·压缩包下载解压
源码_V_saaskw1 小时前
JAVA图文短视频交友+自营商城系统源码支持小程序+Android+IOS+H5
java·微信小程序·小程序·uni-app·音视频·交友
weixin_lynhgworld5 小时前
淘宝扭蛋机小程序系统开发:重塑电商互动模式
大数据·小程序
996幸存者6 小时前
uni-app区域选择、支持静态、动态数据
微信小程序·uni-app
耶啵奶膘11 小时前
uniapp+vue3——通知栏标题纵向滚动切换
uni-app
The_era_achievs_hero14 小时前
UniappDay03
vue.js·微信小程序·uni-app
说私域17 小时前
技术赋能与营销创新:开源链动2+1模式AI智能名片S2B2C商城小程序的流量转化路径研究
人工智能·小程序·开源