一、简介
使用canvas 实现简单的温度刻度表盘,可自定义显示信息和内容,可滑动控制显示。
二、效果图
三、示例代码
<template>
<view class="page">
<view class="progress_box" @touchmove="handleTouchMove">
<canvas class="progress_bar" canvas-id="temperatureCanvas" style="width: 200px; height: 200px;" ></canvas>
</view>
<!-- <view class="progress_box">
<canvas class="progress_bar" canvas-id="temperatureCanvas" style="width: 200px; height: 200px;"></canvas>
</view> -->
<button @click="testFun">test</button>
</view>
</template>
<script>
export default {
data() {
return {
temperature: 20, // 温度值
colors: ['#25baff','#11fcff','#10ffd0', '#00ff7f', '#00ff00','#ff0000'], // 温度对应的颜色
centerx:0,
centery:0,
touchAngle:0,
};
},
mounted() {
this.drawTemperatureScale(200,200,'temperatureCanvas',80,this.temperature);
this.getCanvasPosition();
},
methods: {
getCanvasPosition() {
uni.createSelectorQuery().in(this).select('.progress_bar').boundingClientRect((rect) => {
const { left, top } = rect;
// 计算圆心在整个页面坐标系的 x 和 y 轴坐标
this.centerx = left + rect.width / 2;
this.centery = top + rect.height / 2;
console.log('圆心坐标:', this.centerx, this.centery);
}).exec();
},
//width heigth 圆的直径 scale 刻度表示视图上多少个刻度点 temp 温度数值 也可显示其他的数值
drawTemperatureScale(width,height,canvasId,scale,temp) {
const ctx = uni.createCanvasContext(canvasId, this);
const radius = Math.min(width, height) / 2;
const centerX = width / 2;
const centerY = height / 2;
// 绘制背景圆盘
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
// ctx.setLineWidth(25);
// ctx.setStrokeStyle('#ffffff');
// ctx.stroke();
ctx.setFillStyle('#ffffff');
ctx.fill();
// 绘制刻度线
const temporaryTemp = Math.floor((temp-20)/50*scale)
for (let i = 0; i <= scale; i++) {//外圈刻度
const angle = (i / scale) * (1.6 * Math.PI)+1.2* Math.PI; // 刻度线对应的角度
const startX = centerX + (radius - 10) * Math.sin(angle); //-10 到 -0 间隔5 这个是刻度线的长度
const startY = centerY - (radius - 10) * Math.cos(angle);
const endX = centerX + radius * Math.sin(angle);
const endY = centerY - radius * Math.cos(angle);
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(endX, endY);
// 根据温度设置刻度线颜色
const colorIndex = (i / scale)===1? this.colors.length-1:Math.floor(i / scale * this.colors.length);
if(i <= temporaryTemp){
ctx.setStrokeStyle(this.colors[colorIndex]);
}else{
ctx.setStrokeStyle('#f1f1f1');
}
ctx.setLineWidth(2);//刻度线宽度
ctx.stroke();
}
for (let i = 0; i <= scale; i++) {//内圈刻度
const angle = (i / scale) * (1.6 * Math.PI)+1.2* Math.PI; // 刻度线对应的角度
const startX = centerX + (radius - 23) * Math.sin(angle);//-23 到 -17 间隔5 这个是刻度线的长度
const startY = centerY - (radius - 23) * Math.cos(angle);
const endX = centerX + (radius - 17) * Math.sin(angle);
const endY = centerY - (radius - 17) * Math.cos(angle);
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(endX, endY);
// 根据温度设置刻度线颜色
ctx.setStrokeStyle('#f1f1f1');
ctx.setLineWidth(2);
ctx.stroke();
}
// 绘制圆心位置的文字
const text1 = temp;
const text2 = '℃';
ctx.setFontSize(28); // 设置第一个文字的大小
ctx.setFillStyle('#000');
ctx.setTextAlign('center');
ctx.fillText(text1, centerX - 15, centerY+10);
ctx.setFontSize(18); // 设置第二个文字的大小
ctx.setTextAlign('left');
ctx.fillText(text2, centerX + 5, centerY+10);
//结尾处图标
const iconWidth = 20;
const iconHeight = 20;
const endAngle = (temporaryTemp / scale) * (1.6 * Math.PI)+1.2* Math.PI;
const iconX = (centerX-10) + (radius-10) * Math.sin(endAngle);// -10\-10 与图标位置相关 不同大小的视图需要适当的调整
const iconY = (centerY-10) - (radius-10) * Math.cos(endAngle);// -10\-10 与图标位置相关
ctx.drawImage('../../static/logo.png', iconX, iconY, iconWidth, iconHeight);
ctx.draw();
},
testFun(){
this.temperature+=1;
this.drawTemperatureScale(200,200,'temperatureCanvas',80,this.temperature);
},
handleTouchMove(e) {//根据数值范围自行换算角度关系
const touchX = e.touches[0].clientX;
const touchY = e.touches[0].clientY;
// 计算相对于圆心的角度
this.touchAngle = Math.atan2( touchY-this.centery, touchX - this.centerx)-0.7* Math.PI;
if(touchX < this.centerx && touchY > this.centery && this.touchAngle >= 0 ){
const touchScale = Math.floor(this.touchAngle/(1.6* Math.PI)*50)+20;
this.drawTemperatureScale(200,200,'temperatureCanvas',80,touchScale);
console.log(touchScale)
}else if(this.touchAngle <= -0.4* Math.PI){
const touchScale1 = Math.floor((this.touchAngle+1.7* Math.PI)/(1.6* Math.PI)*50+10)+20;
this.drawTemperatureScale(200,200,'temperatureCanvas',80,touchScale1);
console.log(touchScale1)
}
// console.log('相对圆心的角度:', this.touchAngle-0.7* Math.PI);
}
}
}
</script>
<style>
.page{
position: fixed;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: 100vh;
background-color: #f6f7fb;
}
.progress_box {
position: relative;
width: 100%;
height: 300px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
background-color: aqua;
}
.progress_bar{
position: absolute;
background-color: beige;
}
</style>
代码中有部分注释可借助注释修改,代码逻辑相较简单。
四、优缺点
优点:自定义、样式可多变、参数自调、简单移植、多平台可用
缺点:未限制滑动范围(可自行通过计算移动位置到圆心的距离进行判定)