文章目录


uniapp小程序利用 canvas2d实现根据指定时间动态画圆环效果
在这里插入图片描述

调用

<view class="dubbing-control" :style="{'width':recordWidth,'height':recordWidth}">
	<dubbing-button v-if="show" :width.sync="recordWidth" :size='71' ref="record" @startRecord="onClickHandle" @stopRecord="onClickHandle" :duration="recordConfig.duration"></dubbing-button>
	<image v-else :style="{'width':'142rpx','height':'142rpx','opacity':'0.4'}" src="../static/xgn/record_img.png"></image>
</view>
<script>
export default {
	data() {
		return {
		show:true,//是否展示录制组件
		recordWidth:'88px',//录制按钮+圆环的宽度  动态设置包裹层大小
		recording:'start',//控制是否录制 start:开始 proceed:进行中 end:结束
		}
	}methods:{
	onClickHandle(recording){
		this.recording = recording;
		if (Object.is(this.recording, 'proceed')) {
			//开始渲染圆环
				this.$refs.record.startRecord();
				this.recording = 'proceed';
			} else {
			//停止渲染圆环并重置
				this.recording = 'end';
			}
		}
	}
}
</script>

组件

<template>
	<view class="dubbing-button">
		<view :style="{width: (2*radius+16)+'px', height:(2*radius+16)+'px'}" :class="['record-button',shadowShow ? 'record-button-shadow' : '']  " @touchstart="onClickHandle">
			<canvas class="progress_bg" v-if="Object.is(recording, 'proceed')" type="2d" id="cpbar" canvas-id="cpbar"
				:style="{width: (2*radius+16)+'px', height:(2*radius+16)+'px'}" ></canvas>
			<image v-if="recording=='proceed'" src="../static/xgn/recording_img.gif" :style="{width: 2*radius+'px', height:2*radius+'px'}">
			<image v-else src="../static/xgn/record_img.png" :style="{width: 2*radius+'px', height:2*radius+'px'}">
			</image>
		</view>
		
	</view>

</template>

<script>
	export default {
		name: 'record-button',
		data() {
			return {
				radius: 0, // 半径
				eAngle: 0, // 结束角度
				interval: null,
				deviceWidth: 0,
				recording: 'start',
				canvasContext: null,
				shadowShow: false,
			};
		},
		props: {
			// 进度条转一圈的时间
			duration: {
				type: Number,
				default () {
					return 20000;
				}
			},
			size: { //图片大小
				type: Number,
				default () {
					return 44;
				}
			},
			width:{ //父元素动态设置包裹层的宽高
				type:String,
				default:''
			},
		},
		created() {
			const res = uni.getSystemInfoSync();
			this.deviceWidth = res.windowWidth;
			this.radius = this.getPointValue(this.size);
			this.width =(2*this.radius+16)+'px'; //给父元素设置宽高
			this.$emit('update:width',this.width)
		},
		mounted() {
			// this.canvasContext = uni.createCanvasContext('cpbar', this);
		},
		methods: {
			// 点击录音处理
			async onClickHandle() {
				console.log('button this.recording',this.recording);
				if (!Object.is(this.recording, 'proceed')) {
					this.shadowShow = true;
				} 
				if (Object.is(this.recording, 'proceed')) {
					//如果是录制中 点击则停止
					this.shadowShow = false;
					this.stopRecord();
				} else {
					//否则开始录制 触发父元素录制功能
					if(this.shadowShow){
					this.$emit('startRecord', 'proceed');
					}
				}
			},
			// 开始录音状态
			startRecord() {
				console.log('开始了')
				this.recording = 'proceed';
				let that = this;
				this.shadowShow = false;
				this.drawCircle();
			},
			// 停止录音状态
			stopRecord(status) {
				status ? this.recording = 'start' : this.recording = 'end';
				let that = this;
				this.$emit('stopRecord', 'end');
				clearTimeout(that.interval);
				this.interval = null;
				this.canvasContext && this.clearCicle();
				this.eAngle = 0;
			},
			// 动态绘制圆环
			drawCircle: function() {
				this.$nextTick(()=>{
					const query = uni.createSelectorQuery().in(this);
					query.select('#cpbar').fields({
						node: true,
						size: true
					}).exec(res => {
						console.log('res',res);
						if(res){
							const canvas = res[0].node;
							const ctx = canvas.getContext("2d");
							const dpr = wx.getSystemInfoSync().pixelRatio;
							canvas.width = res[0].width * dpr;
							canvas.height = res[0].height * dpr;
							ctx.scale(dpr, dpr);
							this.canvasContext = ctx;
							this.drawCircleAction();
						}
					})
				})
			},
			drawCircleAction(){
				this.eAngle += 2 * Math.PI / (this.duration / 100);
				this.canvasContext.lineWidth = 4;
				this.canvasContext.strokeStyle = '#FE9B2F';
				this.canvasContext.lineCap = 'butt';
				this.canvasContext.beginPath();
				// arc(x,y,半径,起始弧度,结束弧度,顺逆时针)
				this.canvasContext.arc(this.radius + 8, this.radius + 8, this.radius + 4, 0, this.eAngle , false);
				this.canvasContext.stroke();
				this.canvasContext.closePath();
				if (this.eAngle >= 2 * Math.PI) {
					this.stopRecord();
				} else {
					let that = this;
					clearTimeout(that.interval);
					this.interval = setTimeout(that.drawCircleAction, 100);
				}
			},
			// 清空圆环
			clearCicle() {
				this.canvasContext.clearRect(0, 0, 2 * this.radius + 16, 2 * this.radius + 16);
			},
			// canvas适配不同机型 算出当前屏幕中实际px大小
			getPointValue(val) {
				return Math.floor(val * this.deviceWidth / 750);
			},
		},
		destroyed() {
			this.stopRecord();
		}
	}
</script>

<style lang="scss" scoped>
	.record-button {
		position: relative;
		image {
			position: absolute;
			left: 50%;
			top: 50%;
			transform: translate(-50%,-50%);
		}

		.progress_bg {
			position: absolute;
			top: 0;
			left: 0;
			z-index: 1;
			transform: rotate(-90deg);
		}
	}
	.record-button-shadow{
		image {
			opacity: 0.4;
		}
	}
	.popup_wrap {
		width: 594rpx;
		border-radius: 20rpx;
		background: #fff;
		box-sizing: border-box;
		padding-top: 48rpx;
		display: flex;
		flex-direction: column;
		box-sizing: border-box;
		box-shadow: 0rpx 6rpx 20rpx 0rpx rgba(255, 214, 170, 0.9);

		.popup_title {
			font-size: 34rpx;
			font-weight: 500;
			color: #283445;
			text-align: center;
			margin-bottom: 48rpx;
			padding: 0 20rpx;
			box-sizing: border-box;
		}

		.popup_footer {
			height: 88rpx;
			width: 100%;
			display: flex;
			align-items: center;
			justify-content: center;
			padding: 0 20rpx;
			box-sizing: border-box;

			view {
				flex: 1;
				border-top: 2rpx solid #dcdcdc;
				text-align: center;
				font-size: 32rpx;
				padding: 22rpx 0;
				box-sizing: border-box;

				text {
					display: block;
					height: 50rpx;
				}
			}

			view.popup_footer_confirm {
				color: #283445;

				text {
					border-left: 2rpx solid #dcdcdc;
				}
			}

			view.popup_footer_cancel {
				color: #7e94b1;
			}
		}
	}
</style>

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐