【uniapp 开发】智能温控开关 (环状图)

  • index.vue
<template>
	<view>
		<view class="qiun-columns">
			<uCharts id="ucharts"
					 :val="opts.val"
					 :min="opts.min"
					 :max="opts.max"
					 :step="opts.step"
					 :width="opts.width"
					 :height="opts.height"
					 :border="opts.border"
					 :title="opts.title"
					 :showUnit="opts.showUnit"
					 :unit="opts.unit"
					 :showDecimal="opts.showDecimal"
					 :colorSatrt="opts.colorSatrt"
					 :colorEnd="opts.colorEnd"
					 :colorButton="opts.colorButton"
					 :pageBg="opts.pageBg"
					 :circleBg="opts.circleBg"
					 :pointBg="opts.pointBg"
					 :setUpUrl="opts.setUpUrl"
					 @change="change"
					 ref="ucharts" />
		</view>
		<button class="qiun-button" @tap="changeData()">更新图表</button>
		<!-- 下面是简单调用的例子,除了id和val其他的都可以不给 -->
		<view class="qiun-columns">
			<uCharts id="ucharts2" :val="simple" title="PM2.5" colorSatrt="#5ACECE" :showUnit="showUnit" colorEnd="#8CCE42"/>
		</view>
	</view>
</template>

<script>
import uCharts from '@/components/u-charts/bar.vue';
var _self;

export default {
	data() {
		return {
			simple:18,
			showUnit:false,
			opts:{
				val:15.8,
				min:10,
				max:40,
				step:1,
				width:220,
				height:220,
				border:35,
				title:'室内温度',//传null为不显示最上面标题
				showUnit:true,//是否显示单位
				unit:'℃',
				showDecimal:true,//是否小数
				colorSatrt:'#FFC2B3',
				colorEnd:'#FF3B1D',
				colorButton:'#565656',//底部按钮颜色
				pageBg:'#F4F5F6',//组件页面背景色,如果父组件像本示例设置了padding,你懂的哈
				circleBg:'#FFFFFF',//中间圆心文字块的背景色
				pointBg:'#FFFFFF'//控制点背景色
				// setUpUrl:'../../page/testurl'//设置那个按钮跳转的地方
			}
		};
	},
	components: {
		uCharts
	},
	onLoad() {
		_self = this;
		this.getServerData();
	},
	methods: {
		change(val){
			console.log(val)
		},
		getServerData() {
		},
		changeData() {
			//作为调用组件内方法的示例,您自由想象发挥哈
			this.$refs.ucharts.changeData(28.35);
		}
	}
};
</script>

<style>
.qiun-columns {
	display: flex;
	flex-direction: column !important;
	padding: 40upx;
}
</style>


  • bar.vue

<template>
	<view class="progress_box" :style="{'background-color': pageBg}">
		<canvas :canvas-id="id" :style="{'width':width+'px','height':height+'px'}" disable-scroll=true @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd"></canvas>
		<view class="progress_txt" :style="{'width':centerRadius+'px','height':centerRadius+'px','background-color': circleBg}">
			<view class="progress_info_top" v-if="title">{{title}}</view>
			<view class="progress_info_center">
				<view class="progress_info_center_a">
					{{ integer }}
				</view>
				<view class="progress_info_center_c">
					<view class="progress_info_center_c1"><text class="c1_text" v-if="showUnit">{{unit}}</text></view>
					<view class="progress_info_center_c2"><text class="c1_text" v-if="showDecimal">.{{ decimal }}</text></view>
				</view>
			</view>
			<view class="progress_info_bottom" @tap="goSetUp">设置</view>
		</view>
	</view>
</template>

<script>
function isInAngleRange(angle, startAngle, endAngle) {
	function adjust(angle) {
		while (angle < 0) {
			angle += 2 * Math.PI;
		}
		while (angle > 2 * Math.PI) {
			angle -= 2 * Math.PI;
		}
		return angle;
	}
	angle = adjust(angle);
	startAngle = adjust(startAngle);
	endAngle = adjust(endAngle);
	if (startAngle > endAngle) {
		endAngle += 2 * Math.PI;
		if (angle < startAngle) {
			angle += 2 * Math.PI;
		}
	}
	return angle >= startAngle && angle <= endAngle;
}
function isInArea(e, r) {
	return Math.pow(e.x - r.x, 2) + Math.pow(e.y - r.y, 2) <= Math.pow(r.r, 2);
}
function isInUp(e,r) {
	if(isInArea(e,r)){
		let angle = Math.atan2(r.y - e.y, e.x - r.x);
		angle = -angle;
		if (isInAngleRange(angle, 0.5*Math.PI, 0.75*Math.PI)) {
			return true;
		}else{
			return false;
		}
	}else{
		return false;
	}
}
function isInDown(e,r) {
	if(isInArea(e,r)){
		let angle = Math.atan2(r.y - e.y, e.x - r.x);
		angle = -angle;
		if (isInAngleRange(angle, 0.25*Math.PI, 0.5*Math.PI)) {
			return true;
		}else{
			return false;
		}
	}else{
		return false;
	}
}
function isInCtrl(e,r) {
	if(isInArea(e,r)){
		return true;
	}else{
		return false;
	}
}
export default {
	props:{
		id:{
			default:'ucharts'
		},
		val:{
			default:0
		},
		min:{
			default:-10
		},
		max:{
			default:30
		},
		step:{
			default:1
		},
		width:{
			default:220
		},
		height:{
			default:220
		},
		border:{
			default:35
		},
		title:{
			default:null
		},
		unit:{
			default:'℃'
		},
		showUnit:{
			default:'true'
		},
		showDecimal:{
			default:'true'
		},
		colorSatrt:{
			default:'#FFC2B3'
		},
		colorEnd:{
			default:'#FF3B1D'
		},
		colorButton:{
			default:'#565656'
		},
		pageBg:{
			default:'#F4F5F6'
		},
		circleBg:{
			default:'#FFFFFF'
		},
		pointBg:{
			default:'#FFFFFF'
		},
		setUpUrl:{
			default:'../../page/initUrl'
		}
	},
	data() {
		return {
			valData:0,
			valPoint:{},
			centerPoint:{},
			insideRadius:{},
			startPoint:{},
			endPoint:{},
			isMove:false
		}
	},
	computed: {
		integer:function() {
			if(this.valData<this.min){ return this.min };
			if(this.valData>this.max){ return this.max };
			return parseInt(this.valData);
		},
		decimal:function() {
			if(this.valData<this.min){ return 0 };
			if(this.valData>this.max){ return 0 };
			return Math.abs(parseInt(this.valData*10)-parseInt(this.valData)*10);
		},
		centerRadius:function() {
			return Math.min(this.width/2-this.border,this.height/2-this.border)*2 * 0.9;
		}
	},
	mounted: function() {
		this.valData=this.val;
		this.drawCircle(this.val,this.min,this.max,this.width,this.height,this.border,this.colorSatrt,this.colorEnd,this.colorButton,this.pointBg);
	},
	methods: {
		drawCircle:function(val,min,max,width,height,border,colorSatrt,colorEnd,colorButton,pointBg) {
			let radius=Math.min(width/2-border/2,height/2-border/2);
			let centerPoint={
				x:width/2,
				y:height/2,
				r:radius+border/2
			};
			this.centerPoint=centerPoint;
			this.insideRadius={
				x:width/2,
				y:height/2,
				r:radius-border/2
			}
			let ctx = uni.createCanvasContext(this.id, this);
			ctx.setLineWidth(border);
			ctx.setStrokeStyle(colorButton);
			ctx.setLineCap('butt');
			//画按钮背景
			ctx.beginPath();
			ctx.arc(centerPoint.x, centerPoint.y, radius , 0.25 * Math.PI, 0.75 * Math.PI, false);
			ctx.stroke();
			//画按钮
			ctx.setLineWidth(1);
			ctx.setStrokeStyle("#FFFFFF");
			ctx.beginPath();
			ctx.moveTo(centerPoint.x, height);
			ctx.lineTo(centerPoint.x, height-border);
			ctx.stroke();
			ctx.setLineWidth(3);
			ctx.beginPath();
			let upPoint={
				x:centerPoint.x+(radius)*Math.cos(0.625* Math.PI),
				y:centerPoint.x+(radius)*Math.sin(0.625* Math.PI)
			};
			let downPoint={
				x:centerPoint.x+(radius)*Math.cos(0.375* Math.PI),
				y:centerPoint.x+(radius)*Math.sin(0.375* Math.PI)
			};
			let xLength=border*0.6/2;
			ctx.moveTo(upPoint.x-xLength, upPoint.y);
			ctx.lineTo(upPoint.x+xLength, upPoint.y);
			ctx.moveTo(upPoint.x, upPoint.y-xLength);
			ctx.lineTo(upPoint.x, upPoint.y+xLength);
			ctx.moveTo(downPoint.x-xLength, downPoint.y);
			ctx.lineTo(downPoint.x+xLength, downPoint.y);
			ctx.stroke();
			//变色背景条
			ctx.setLineWidth(border);
			let gradient = ctx.createLinearGradient(centerPoint.x-radius-border, centerPoint.y, centerPoint.x+radius+border, centerPoint.y);
			gradient.addColorStop('0', colorSatrt);
			gradient.addColorStop('1.0', colorEnd);
			ctx.setStrokeStyle(gradient);
			ctx.beginPath();
			ctx.arc(centerPoint.x, centerPoint.y, radius , 0.75 * Math.PI, 0.25 * Math.PI, false);
			ctx.stroke();
			//画控制点
			ctx.beginPath();
			ctx.setFillStyle(pointBg);
			//控制点阴影效果,不需要可以删掉
			ctx.setShadow(2, 2, 2, '#888888')
			if(val<min) val=min;
			if(val>max) val=max;
			let progress = (val- min) / (max - min) ;
			//控制点半径
			let valRadius=border * 0.45;
			//控制点弧度
			let valRadian= valRadius / (Math.PI * radius);
			progress = (1.5-2*valRadian) * progress + 0.75+valRadian;
			if (progress >= 2) {
				progress = progress % 2;
			}
			let valPoint={
				x:centerPoint.x+(radius)*Math.cos(progress* Math.PI),
				y:centerPoint.x+(radius)*Math.sin(progress* Math.PI),
				r:valRadius,
				v:val,
				s:0.75+valRadian,
				e:2.25-valRadian,
				t:1.5-2*valRadian,
				n:progress,
			};
			this.valPoint=valPoint;
			ctx.arc(valPoint.x, valPoint.y, valPoint.r, 0, 2 * Math.PI, false);
			ctx.closePath();
			ctx.fill();
			ctx.draw();
		},
		touchStart:function(e) {
			let touches = e.mp.changedTouches[0] || e.changedTouches[0];
			if(isInCtrl(touches,this.valPoint)){
				this.isMove=true;
				touches.val=this.valPoint;
				this.startPoint=touches;
			}
		},
		touchMove:function(e) {
			let touches = e.mp.changedTouches[0] || e.changedTouches[0];
			if(this.isMove === true){
				//这两句是判断是否在进度条内,加上体验不好,你可以试一下&& isInArea(touches,this.insideRadius) === false && isInArea(touches,this.centerPoint) === true
				let angle = Math.atan2(this.centerPoint.y - touches.y, touches.x - this.centerPoint.x);
				angle = -angle;
				let newRadian = angle/Math.PI;
				if(newRadian<0){
					newRadian = 2 + newRadian;
				}
				if(newRadian < this.startPoint.val.e - 1.7){
					newRadian += 2;
				}
				let progress = (newRadian - this.startPoint.val.s)/this.startPoint.val.t;
				progress = (this.max - this.min)*progress;
				let nweVal = this.min + progress;
				if(nweVal>this.max) nweVal = this.max;
				if(nweVal<this.min) nweVal = this.min;
				nweVal=(parseInt(nweVal*10)*0.1).toFixed(1);
				this.valData = nweVal;
				this.drawCircle(nweVal,this.min,this.max,this.width,this.height,this.border,this.colorSatrt,this.colorEnd,this.colorButton,this.pointBg);
			}
		},
		touchEnd:function(e) {
			let touches = e.mp.changedTouches[0] || e.changedTouches[0];
			if(isInUp(touches,this.centerPoint) === true && this.isMove === false){
				this.valData+=this.step;
				if(this.valData>this.max) this.valData = this.max;
				this.drawCircle(this.valData,this.min,this.max,this.width,this.height,this.border,this.colorSatrt,this.colorEnd,this.colorButton,this.pointBg);
			}
			if(isInDown(touches,this.centerPoint) === true && this.isMove === false){
				this.valData-=this.step;
				if(this.valData<this.min) this.valData = this.min;
				this.drawCircle(this.valData,this.min,this.max,this.width,this.height,this.border,this.colorSatrt,this.colorEnd,this.colorButton,this.pointBg);
			}
			if(this.isMove){
				this.$emit('change',this.valData)
				// let _this=this;
				// uni.request({
				// 	url: 'https://www.ucharts.cn/data.json',
				// 	data:{
				// 		val:this.valData
				// 	},
				// 	success: function(res) {
				// 		console.log("发送新数据["+_this.valData+"]成功!")
				// 	},
				// 	fail: () => {
				// 		_self.tips="网络错误,小程序端请检查合法域名";
				// 	},
				// });
				// this.isMove = false;
			}
		},
		goSetUp:function() {
			console.log(this.setUpUrl);
			uni.showToast({
				title: '跳转界面',
				duration: 2000
			});
		},
		changeData:function(val) {
			this.valData=val;
			this.drawCircle(val,this.min,this.max,this.width,this.height,this.border,this.colorSatrt,this.colorEnd,this.colorButton,this.pointBg);
		},
	}
};
</script>

<style>
	.progress_box {
		position: relative;
		width: 100%;
		height: 100%;
		display: flex;
		align-items: center;
		justify-content: center;
		text-align: center;
	}
	.progress_txt {
		position: absolute;
		font-size: 28upx;
		border-radius: 50%;
		flex-direction: column !important;
		display: flex;
		align-items: center;
		justify-content: center;
		text-align: center;
		box-shadow:0 0 6upx 4upx #DEDEDE;
	}
	.progress_info_top {
		font-size: 32upx;
		letter-spacing: 2upx;
		color: #000000;
	}
	.progress_info_center {
		display: flex;
		flex-direction: row !important;
		color: #000000;
	}
	.progress_info_center_a {
		font-size: 80upx;
		height: 100upx;
		line-height: 100upx;
		font-weight: bold;
		letter-spacing: 2upx;
	}
	.progress_info_center_c {
		position: relative;
		height: 100upx;
		display: flex;
		flex-direction: column !important;
		align-items: center;
		justify-content: center;
	}
	.progress_info_center_c1 {
		display: flex;
		height: 50upx;
		align-items: center ;
	}
	.c1_text{
		height: 28upx;
		font-size: 28upx;
		letter-spacing: 2upx;
	}
	.progress_info_center_c2 {
		display: flex;
		height: 50upx;
		align-items:flex-start;
	}
	.c2_text{
		height: 50upx;
		font-size: 28upx;
		letter-spacing: 2upx;
	}
	.progress_info_bottom {
		font-size: 32upx;
		letter-spacing: 2upx;
		color: #666666;
	}
</style>



  • qiun.css

page {
	background: #F4F5F6;
	width: 750upx;
	overflow-x: hidden;
}

.qiun-padding {
	padding: 2%;
	width: 96%;
}

.qiun-wrap {
	display: flex;
	flex-wrap: wrap;
}

.qiun-rows {
	display: flex;
	flex-direction: row !important;
}

.qiun-columns {
	display: flex;
	flex-direction: column !important;
}

.qiun-common-mt {
	margin-top: 10upx;
}

.qiun-common-border-bottom {
	border-bottom: 1px solid #E9E9E9;
}

.qiun-bg-white {
	background: #FFFFFF;
}

.qiun-title-bar {
	width: 96%;
	padding: 10upx 2%;
	flex-wrap: nowrap;
}

.qiun-title-dot-light {
	border-left: 10upx solid #0ea391;
	padding-left: 10upx;
	font-size: 32upx;
	color: #000000
}

.qiun-textarea {
	height: 400upx;
	font-size: 34upx;
	box-sizing: border-box;
	line-height: 50upx;
	width: 100%;
	background-color: #FFFFFF;
}

.qiun-text-tips {
	font-size: 28upx;
	color: #dc2626;
	line-height: 40upx;
	padding: 6upx;
}

.qiun-button {
	background: #2fc25b;
	color: #FFFFFF;
	margin: 20upx;
}

/* 通用样式 */
.qiun-charts {
	width: 750upx;
	height: 500upx;
	background-color: #FFFFFF;
}

.charts {
	width: 750upx;
	height: 500upx;
	background-color: #FFFFFF;
}

/* 横屏样式 */
.qiun-charts-rotate {
	width: 700upx;
	height: 1100upx;
	background-color: #FFFFFF;
	padding: 25upx;
}

.charts-rotate {
	width: 700upx;
	height: 1100upx;
	background-color: #FFFFFF;
}

/* 圆弧进度样式 */
.qiun-charts3 {
	width: 750upx;
	height: 250upx;
	background-color: #FFFFFF;
	position: relative;
}

.charts3 {
	position: absolute;
	width: 250upx;
	height: 250upx;
	background-color: #FFFFFF;
}


posted @ 2019-09-17 18:33  浪里小白龙呼呼呼  阅读(1308)  评论(0编辑  收藏  举报