Canvas in uniapp draws arc sliders

foreword

 At the beginning, an arc slider was implemented with Vue, because uniapp itself encapsulates the canvas method, which is not applicable when transplanted to uniapp. So it was modified.

accomplish

<canvas style="height: 300px; width: 300px;" ref="myCanvas" canvas-id="myCanvas" @touchstart="canvasDown" @touchmove="canvasMove" @touchend="canvasUp"></canvas>
<script>
import {
    
    
	hideTabBar
} from "@/common/util.js"
import slideableImg from "@/static/settings/slideable.png"
export default {
    
    
	data() {
    
    
		return {
    
    
			pageUrl: {
    
    
				info: 'device/loadDevice', // 设备信息
				edit: 'appDevice/singleSetting' // 保存设备信息
			},
			// 画布
			canvas: '',
			// 画笔
			ctx: '',
			// 是否可以拖动
			canvasMoveUse: false,
			// 可滑动提醒图片
			slideableImg,
			// 显示的电量百分比
			showCharge: 50,
			// 设备信息
			deviceInfo: {
    
    },
			// 设备id
			deviceId: '',
			// 用户id
			userId: ''
		}
	},
	
	created() {
    
    
		hideTabBar()
	},
	computed: {
    
    
		// 显示百分比
		showChargePercent(){
    
    
			return this.showCharge + '%'
		}
	},
	onLoad() {
    
    
		this.deviceId = uni.getStorageSync('deviceId')
		this.userId = uni.getStorageSync('consumer').id
		this.$nextTick(() => {
    
    
			this.initCanvas()
		})
	},
	onShow() {
    
    
		uni.setNavigationBarTitle({
    
    
			title: this.$t('function.deviceDischarge')
		});
		// 获取设备信息
		// this.getDischargeInfo()
	},
	// 监听返回事件
	onBackPress() {
    
    
	},
	methods: {
    
    
		// 初始化
		async initCanvas() {
    
    
			await this.getDischargeInfo()
			let realAngel = (0.01 * (Math.PI * 5 / 3) * this.showCharge) + ((Math.PI * 2) / 3)
			let x = this.getInitX(150, 150, 100, this.showCharge)
			let y = this.getInitY(150, 150, 100, this.showCharge)
			this.ctx = uni.createCanvasContext("myCanvas")
			this.drawDefaultRing()
			// this.drawRingByTouch(Math.PI * 9 / 6)
			this.drawRingByTouch(realAngel)
			this.drawPoint(x, y)
			this.drawText(this.showChargePercent)
			this.ctx.draw()
		},

		// 画默认的圆弧	
		drawDefaultRing() {
    
    
			this.ctx.beginPath()
			this.ctx.setLineWidth(20)
			this.ctx.setStrokeStyle('#fff')
			this.ctx.arc(
				150, 150,
				100,
				0, Math.PI * 2 / 6,
				false
			);
			this.ctx.stroke();
			this.ctx.beginPath()
			this.ctx.arc(
				150, 150,
				100,
				Math.PI * 4 / 6, Math.PI * 14 / 6,
				false
			);
			this.ctx.stroke();
		},

		// 画圆点
		drawPoint(x, y) {
    
    
			this.ctx.beginPath()
			this.ctx.setFillStyle('#009f8a')
			this.ctx.arc(
				x, y,
				9,
				0, Math.PI * 2
			)
			this.ctx.fill();

			this.ctx.beginPath()
			this.ctx.setFillStyle('#ffffff')
			this.ctx.arc(
				x, y,
				4,
				0, Math.PI * 2
			)
			this.ctx.fill();
		},

		// 画圆弧
		drawRingByTouch(angle) {
    
    
			this.ctx.beginPath()
			this.ctx.setLineWidth(8)
			var gr = this.ctx.createLinearGradient(80, 150, 220, 150);
			//添加颜色端点
			gr.addColorStop(0, 'rgba(12, 194, 194, 1)');
			gr.addColorStop(.24, 'rgba(46, 163, 255, 1)');
			gr.addColorStop(.58, 'rgba(74, 115, 247, 1)');
			gr.addColorStop(.78, 'rgba(74, 115, 247, 1)');
			gr.addColorStop(1, 'rgba(139, 71, 222, 1)');
			//应用fillStyle生成渐变
			this.ctx.setStrokeStyle(gr);
			this.ctx.arc(
				150, 150,
				100,
				(Math.PI * 4 / 6), angle,
				false
			);
			this.ctx.stroke();
		},

		// 写文字
		drawText(text) {
    
    
			// 文字下方底色
			this.ctx.beginPath()
			this.ctx.setFillStyle('#ffffff')
			this.ctx.arc(
				150, 150,
				70,
				0, Math.PI * 2
			)
			this.ctx.fill();

			this.ctx.beginPath()
			// text = '75%';
			//字体颜色
			this.ctx.font = '30px arial';
			this.ctx.fillStyle = '#000000';
			// 位置
			this.ctx.textAlign = 'center';
			this.ctx.textBaseline = 'middle';
			//描边宽度
			this.ctx.lineWidth = 3;
			//填充文字
			this.ctx.fillText(text, 150, 150);
		},

		canvasDown(e) {
    
    
			this.canvasMoveUse = true;
		},
		canvasMove(e) {
    
    
			if (this.canvasMoveUse) {
    
    
				let canvasX = e.touches[0].x
				let canvasY = e.touches[0].y

				// console.log(canvasX + ',' + canvasY);
				let angle = Math.atan((canvasY - 150) / (canvasX - 150))

				let x = this.getX(150, 150, 100, canvasX, canvasY)
				let y = this.getY(150, 150, 100, canvasX, canvasY)

				let realAngle = this.getRealAngle(canvasX, canvasY, angle)
				// 半圆的滑动范围判断
				if (realAngle <= (Math.PI * 14 / 6) && realAngle >= (Math.PI * 9 / 6)) {
    
    
					let percent = ((realAngle - (2 * Math.PI / 3)) / (Math.PI * 5 / 3)).toFixed(2)
					let showPercent = (percent * 100).toFixed(0)
					this.showCharge = showPercent
					// console.log(realAngle);
					this.$nextTick(() => {
    
    
						this.ctx.clearRect(0, 0, 300, 300)
						this.drawDefaultRing()
						this.drawRingByTouch(realAngle)
						this.drawPoint(x, y)
						this.drawText(this.showChargePercent)
						this.ctx.draw()
					})
				}
			}
		},

		/**
		 * 获取角度
		 * @param canvasX 鼠标x坐标
		 * @param canvasY 鼠标y坐标
		 * @param angle 根据反正切函数算出的角度
		 */
		getRealAngle(canvasX, canvasY, angle) {
    
    
			let realAngle = 0
			if (canvasX < 150 && canvasY > 150) {
    
    
				realAngle = (Math.PI / 2) - Math.abs(angle) + Math.PI / 2
			}
			if (canvasX < 150 && canvasY < 150) {
    
    
				realAngle = Math.abs(angle) + (Math.PI / 2) + Math.PI / 2
			}
			if (canvasX > 150 && canvasY < 150) {
    
    
				realAngle = (Math.PI / 2) - Math.abs(angle) + Math.PI + Math.PI / 2
			}
			if (canvasX > 150 && canvasY > 150) {
    
    
				realAngle = Math.abs(angle) + Math.PI * (3 / 2) + Math.PI / 2
			}
			return realAngle
		},

		/**
		 * 获取在圆上x轴坐标
		 * @param ox 原点x坐标
		 * @param oy 原点y坐标
		 * @param r 圆的半径
		 * @param x 鼠标x轴坐标
		 * @param y 鼠标y轴坐标
		 */
		getX(ox, oy, r, x, y) {
    
    
			// 角度
			let angle = Math.atan((oy - y) / (x - ox))
			// x轴长度
			let realX = Math.abs(r * Math.cos(angle))
			if (x < ox) {
    
    
				return (-realX) + ox
			} else {
    
    
				return realX + ox
			}
		},

		/**
		 * 根据初始值获取初始的x
		 * @param ox 原点x坐标
		 * @param oy 原点y坐标
		 * @param r 半径 
		 * @param angle
		 */
		getInitX(ox, oy, r, angle) {
    
    
			let tempAngle = (angle * (Math.PI * 5 / 3 * 0.01) + Math.PI / 6)
			if (tempAngle <= Math.PI) {
    
    
				return (-Math.abs(r * Math.sin(tempAngle))) + ox
			} else if (tempAngle > Math.PI) {
    
    
				return Math.abs(r * Math.sin(tempAngle)) + ox
			}
		},

		/**
		 * 根据初始值获取初始y
		 * @param ox 原点x坐标
		 * @param oy 原点y坐标
		 * @param {Object} r 半径
		 * @param {Object} angle 角度
		 */
		getInitY(ox, oy, r, angle) {
    
    
			let tempAngle = (angle * (Math.PI * 5 / 3) * 0.01 + Math.PI / 6)
			if ((tempAngle <= Math.PI / 2) || (tempAngle >= Math.PI * 3 / 2)) {
    
    
				return oy + Math.abs(r * Math.cos(tempAngle))
			} else if ((tempAngle > Math.PI / 2) && (tempAngle < Math.PI * 3 / 2)) {
    
    
				return oy - Math.abs(r * Math.cos(tempAngle))
			}
		},

		/**
		 * 获取在圆上y轴坐标
		 * @param ox 原点x坐标
		 * @param oy 原点y坐标
		 * @param r 圆的半径
		 * @param x 鼠标x轴坐标
		 * @param y 鼠标y轴坐标
		 */
		getY(ox, oy, r, x, y) {
    
    
			// 角度
			let angle = Math.atan((oy - y) / (x - ox))
			// y轴长度
			let realY = Math.abs(r * Math.sin(angle))
			if (y < oy) {
    
    
				return (-realY + oy)
			} else {
    
    
				return realY + oy
			}
		},

		canvasUp(e) {
    
    
			this.canvasMoveUse = false
		},
		canvasLeave(e) {
    
    
			this.canvasMoveUse = false
		},

		/**
		 * 获取设备放电信息
		 */
		getDischargeInfo() {
    
    
			let params = {
    
    
				id: this.deviceId
			}
			return this.$http('GET', this.pageUrl.info, params)
				.then(res => {
    
    
					if (res.success) {
    
    
						if(this.showCharge){
    
    
							this.deviceInfo = res.result // 保存设备信息
							this.showCharge = Number(res.result.battery_low_discharge)
						}
					} else {
    
    
						this.$showToast(res.message)
					}
				})
				.catch(err => {
    
    
					this.$showToast(err.message)
				})
		},

		// 保存
		save() {
    
    
			let params = {
    
    
				id: this.deviceId,
				battery_low_discharge: Number(this.showCharge) ,
				user_id: this.userId
			}
			this.$http('POST', this.pageUrl.edit, params)
			.then(res => {
    
    
				if(res.success){
    
    
					this.$showToast(res.message)
				}else{
    
    
					this.$showToast(res.message)
				}
			})
			.catch(err => {
    
    
				this.$showToast(err.message)
			})
		}
	}
}
</script>

Effect:
insert image description here

Guess you like

Origin blog.csdn.net/m0_72791534/article/details/125828638