标签:tpi function div arm 长度 min assets ext eth
系统需要添加智能消防栓模块。集成了一家采用NbIOT通讯的智能消防栓产品。由第厂家平台对接NbIot特联网平台,我们平台提供一个api从第三方平台接收消防栓状态,用SignlaR把状态推送到前端。需要写一个状态实时同步界面,包括倾斜报警、盖子打开报警、出水告警等。
先看下测试效果图。
附Vue源码
<template> <div style="color:white;height:100%;"> <div id="todetail"> <router-link :to="`/admin/stand/commondevice?sid=${$route.query.sid}`">{{this.$t("详情")}}</router-link> </div> <div class="detailview"> <div class="detailview_1"> <div> <box style="position:relative"> <div id="centerTop" style="position:absolute;top:20px;left:20px"> <!-- <div style="font-size:1.5rem">{{this.$t("消防栓检测")}}</div> --> <div style="font-size:1.5rem">测试</div> <div>盖子打开<input v-model="animate.cap.angel" /> 盖子角度{{animate.cap.currentAngel}}</div> <div> 栓柱倾斜<input v-model="animate.bolt.angel" /> 倾斜角度{{animate.bolt.currentAngel}}</div> <div> <b-form-checkbox switch size="lg" v-model="animate.water.action">放水</b-form-checkbox> </div> </div> <div id="leftTop" style="width:100%;height:100%"></div> </box> </div> </div> <div class="detailview_1"> <div> <box id="canvas_box" style="position:relative"> <div :style="canvasStyle"> <canvas ref="canvas_bolt" style="position:absolute;left:0;bottom:0;"></canvas> <canvas ref="canvas_cap" style="position:absolute;left:0;bottom:0;"></canvas> <canvas ref="canvas_water" style="position:absolute;left:0;bottom:0;"></canvas> <canvas ref="canvas_wall" style="position:absolute;left:0;bottom:0;"></canvas> </div> </box> </div> </div> <div class="detailview_1"> <div> <box> </box> </div> </div> <div class="detailview_2"> <div> <box> </box> </div> </div> </div> <img :src="boltData.pic" id="boltPic" @load="drawBolt(false)" hidden/> <img :src="capData.pic" id="capPic" @load="drawCap(false)" hidden/> <img :src="wallData.pic" id="wallPic" @load="drawWall" hidden/> </div> </template> <script> import box from "@/components/box.vue"; import alarm from "@/components/alarm"; export default { components: { box, alarm }, data: function() { return { /* 动画参数配置 */ animate: { /* 盖子动画参数配置 */ cap: { thread: null,//保存定时器 angel: 0,//盖子角度 currentAngel: 0,//当前角度 speed: 20 //盖子角度变化的速度 }, bolt: { thread: null, angel: 0, currentAngel: 0, speed: 20 }, water: { thread: null, rate: 0, speed: 0, action: false } }, /**画布父元素的css */ canvasStyle: { position: "absolute", left: 0, bottom: 0, width: "100%", height: "100%" }, /**画布初始化参数 */ initData: { /**原图尺寸 */ size: { width: 340, height: 380 }, /**渲染尺寸 */ rendSize: { width: 0, height: 0 } }, /**底部墙渲染数据 */ wallData: { //画片 pic: require("@/assets/img/hr/ic_di.png"), //canvas上下文 ctx: null, //原图尺寸 size: { width: 340, height: 166 }, //渲染尺寸 rendSize: { width: 0, height: 0 }, //起点坐标 startPos: { x: 0, y: 0 } }, //栓柱渲染数据 boltData: { pic: require("@/assets/img/hr/ic_xfx.png"), //图片 ctx: null,//canvas上下文 offsetX: 45,//X轴偏移量 RBX: 160,//栓柱右下角X轴偏移量 offsetRBX: 160,//栓柱右下角X轴渲染后偏移量 //原图尺寸 size: { width: 127, height: 216 }, //渲染尺寸 rendSize: { width: 0, height: 0 }, //起点坐标 startPos: { x: 0, y: 0 } }, //盖子渲染数据 capData: { pic: require("@/assets/img/hr/ic_xfx_gz.png"),//图片 ctx: null,//canvas上下文 offsetX: 172,//x轴偏移量 offsetY: 72,//y軕偏移量 //原图尺寸 size: { width: 20, height: 46 }, //渲染尺寸 rendSize: { width: 0, height: 0 }, //起点坐标 startPos: { x: 0, y: 0 } }, //水滴渲染数据 waterData: { ctx: null,//canvas上下文 //渲染尺寸 rendSize: { width: 0, height: 0 } } }; }, mounted: function() { this.$nextTick(() => { //初始化 this.initCanvas(); window.addEventListener("resize", () => { //浏览器尺寸大小改变后重新渲染 this.initCanvas(true); }); }); }, watch: { //监控盖子的角度 "animate.cap.angel": function() { this.startCapAnimate(); }, //监控栓柱的角度 "animate.bolt.angel": function() { this.startBoltAnimate(); }, //监控是否放水 "animate.water.action": function() { //把柱子立正 this.animate.bolt.currentAngel = 0; this.animate.bolt.angel = 0; //开盖子 this.animate.cap.currentAngel = 90; this.animate.cap.angel = 90; if (this.animate.water.action) { //放水 this.startWater(); } else { //停水 window.clearInterval(this.animate.water.thread); this.waterData.ctx.clearRect( 0, 0, this.initData.rendSize.width, this.initData.rendSize.height ); //关盖子 this.animate.cap.angel = 0; } } }, methods: { initCanvas(isDraw) { //调整画布位置及大小 //父元素大小 var parentWidth = document.getElementById("canvas_box").clientWidth; var parentHeight = document.getElementById("canvas_box").clientHeight; //画布宽度最大值为父元素宽度 this.initData.rendSize.width = this.initData.size.width > parentWidth ? parentWidth : this.initData.size.width; //根据比率设置画布高度 var rate = this.initData.size.width / this.initData.size.height; this.initData.rendSize.height = this.initData.size.height / rate; //画面高度最大值为父元素高度 if (this.initData.rendSize.height > parentHeight) { this.initData.rendSize.height = parentHeight; //再次调整宽度 this.initData.rendSize.width = this.initData.rendSize.height * rate; } //设置画布大小 this.canvasStyle.width = `${this.initData.rendSize.width}px`; this.canvasStyle.height = `${this.initData.rendSize.height}`; if (this.initData.rendSize.width < parentWidth) { //画布居中 var diff = parentWidth - this.initData.rendSize.width; this.canvasStyle.left = `${diff / 2}px`; } //设置最底部墙的渲染数据 var rateWall = this.wallData.size.width / this.wallData.size.height; this.wallData.rendSize.width = this.initData.rendSize.width; this.wallData.rendSize.height = this.initData.rendSize.width / rateWall; this.wallData.startPos.x = 0; this.wallData.startPos.y = this.initData.rendSize.height - this.wallData.rendSize.height; this.$refs.canvas_wall.width = this.initData.rendSize.width; this.$refs.canvas_wall.height = this.initData.rendSize.height; this.wallData.ctx = this.$refs.canvas_wall.getContext("2d"); //设置消防栓的渲染数据 var rateBolt = this.boltData.size.width / this.boltData.size.height; var rateBoltWidth = this.boltData.size.width / this.initData.size.width; this.boltData.rendSize.width = this.initData.rendSize.width * rateBoltWidth; this.boltData.rendSize.height = this.boltData.rendSize.width / rateBolt; var rateBoltOffset = this.initData.size.width / this.initData.rendSize.width; this.boltData.startPos.x = this.boltData.offsetX / rateBoltOffset; this.boltData.offsetRBX = this.boltData.RBX / rateBoltOffset; this.boltData.startPos.y = this.initData.rendSize.height - (this.wallData.rendSize.height + this.boltData.rendSize.height); this.$refs.canvas_bolt.width = this.initData.rendSize.width; this.$refs.canvas_bolt.height = this.initData.rendSize.height; this.boltData.ctx = this.$refs.canvas_bolt.getContext("2d"); //设置盖子的渲染数据 var rateCap = this.capData.size.width / this.capData.size.height; var rateCapWidth = this.capData.size.width / this.initData.size.width; this.capData.rendSize.width = this.initData.rendSize.width * rateCapWidth; this.capData.rendSize.height = this.capData.rendSize.width / rateCap; this.capData.startPos.x = this.capData.offsetX / rateBoltOffset; this.capData.startPos.y = this.capData.offsetY / (this.boltData.size.height / this.boltData.rendSize.height) + this.boltData.startPos.y; this.$refs.canvas_cap.width = this.initData.rendSize.width; this.$refs.canvas_cap.height = this.initData.rendSize.height; this.capData.ctx = this.$refs.canvas_cap.getContext("2d"); //设置水滴渲染数据 this.$refs.canvas_water.width = this.waterData.rendSize.width = this.initData.rendSize.width; this.$refs.canvas_water.height = this.waterData.rendSize.height = this.initData.rendSize.height; this.waterData.ctx = this.$refs.canvas_water.getContext("2d"); //页面加载初始化不能画,因为可能图片还没有加载出来。 //浏览器大小调整时可以画,因为图片已经加载完毕 if (isDraw) { this.drawWall(); this.drawBolt(); this.drawCap(); } }, /* 画底部的墙 */ drawWall() { this.wallData.ctx.drawImage( document.getElementById("wallPic"), this.wallData.startPos.x, this.wallData.startPos.y, this.wallData.rendSize.width, this.wallData.rendSize.height ); }, /* 盏栓柱 angel:栓柱倾斜角度 */ drawBolt(angel) { angel = angel || this.animate.bolt.angel; this.boltData.ctx.clearRect( 0, 0, this.initData.rendSize.width, this.initData.rendSize.height ); this.boltData.ctx.save(); //以栓柱右下角为旋转基点 this.boltData.ctx.translate( this.boltData.offsetRBX, this.initData.rendSize.height - this.wallData.rendSize.height ); this.boltData.ctx.rotate(angel * Math.PI / 180); //以栓柱右下角为中心点,原来的起点坐标灰新坐标系中的位置 var cx = -(this.boltData.offsetRBX - this.boltData.startPos.x); var cy = -this.boltData.rendSize.height; this.boltData.ctx.drawImage( document.getElementById("boltPic"), cx, cy, this.boltData.rendSize.width, this.boltData.rendSize.height ); this.boltData.ctx.restore(); //第次还要调整一下盖子的位置,让盖子跟栓柱一起倾斜 this.drawCap(null, this.animate.bolt.currentAngel); }, /* 画盖子 angel:盖子角度 boltAngel:栓柱角度,用于计算盖子的起点位置 */ drawCap(angel, boltAngel) { angel = angel || this.animate.cap.angel; boltAngel = boltAngel || this.animate.bolt.angel; this.capData.ctx.save(); //清空内容 this.capData.ctx.clearRect( 0, 0, this.initData.rendSize.width, this.initData.rendSize.height ); //把控制点放到栓柱的右下角 this.capData.ctx.translate( this.boltData.offsetRBX, this.initData.rendSize.height - this.wallData.rendSize.height ); //以栓柱右下角为旋转基点,旋转和栓柱一样的角度,这样不管栓柱倾斜到什么角度,盖子也能找到他的起点应该在的位置 this.capData.ctx.rotate(boltAngel * Math.PI / 180); //旋转完后控制中心点坐标加到原来的位置 this.capData.ctx.translate( -this.boltData.offsetRBX, -this.initData.rendSize.height + this.wallData.rendSize.height ); //再把中心点放点盖子的起点位置 this.capData.ctx.translate( this.capData.startPos.x, this.capData.startPos.y ); //盖子的角度 var rotage = angel * Math.PI / 180 * -1; this.capData.ctx.rotate(rotage); this.capData.ctx.drawImage( document.getElementById("capPic"), 0, 0, this.capData.rendSize.width, this.capData.rendSize.height ); //画完后回到保存前的状态 this.capData.ctx.restore(); }, /* 栓柱倾斜效果 */ startBoltAnimate() { if (this.animate.bolt.thread) { window.clearInterval(this.animate.bolt.thread); } this.animate.bolt.thread = window.setInterval(() => { var exit = false; //变化currentAngel,直到等于angel if (this.animate.bolt.angel > this.animate.bolt.currentAngel) { this.animate.bolt.currentAngel += 1; if (this.animate.bolt.currentAngel > this.animate.bolt.angel) { exit = true; } } else { this.animate.bolt.currentAngel -= 1; if (this.animate.bolt.currentAngel < this.animate.bolt.angel) { exit = true; } } if (exit) { this.animate.bolt.currentAngel = this.animate.bolt.angel * 1; } //画栓柱,角度为currentAngel。每次角度累加或累减1 this.drawBolt(this.animate.bolt.currentAngel); if (exit) { window.clearInterval(this.animate.bolt.thread); this.animate.bolt.thread = null; } }, this.animate.bolt.speed); }, /* 盖子角度变化效果。 callBack:完成后的回调 */ startCapAnimate(callBack) { if (this.animate.cap.thread) { window.clearInterval(this.animate.cap.thread); } this.animate.cap.thread = window.setInterval(() => { var exit = false; //变化currentAngel,直到等于angel if (this.animate.cap.angel > this.animate.cap.currentAngel) { this.animate.cap.currentAngel += 1; if (this.animate.cap.currentAngel > this.animate.cap.angel) { exit = true; } } else { this.animate.cap.currentAngel -= 1; if (this.animate.cap.currentAngel < this.animate.cap.angel) { exit = true; } } if (exit) { this.animate.cap.currentAngel = this.animate.cap.angel * 1; } //画盖子,角度为currentAngel。每次角度累加或累减1 this.drawCap(this.animate.cap.currentAngel); if (exit) { window.clearInterval(this.animate.cap.thread); this.animate.cap.thread = null; if (callBack) { callBack(); } } }, this.animate.cap.speed); }, /* 开始放水 */ startWater() { if (this.animate.water.thread) { window.clearInterval(this.animate.water.thread); } this.animate.water.thread = window.setInterval(() => { //控制变量累加,最大值为1 this.animate.water.rate += 0.03; if (this.animate.water.rate >= 1) { this.animate.water.rate = 0; } //画水流 this.drawPullWater(this.animate.water.rate); }); }, /* 画水流 rate:控制变量 */ drawPullWater(rate) { //先清除画布 this.waterData.ctx.clearRect( 0, 0, this.initData.rendSize.width, this.initData.rendSize.height ); //填充水流颜色 this.waterData.ctx.strokeStyle = "rgba(2,180,245,0.8)"; this.waterData.ctx.fillStyle = "rgba(2,180,245,0.8)"; this.waterData.ctx.beginPath(); //一次画4个水滴,水滴从盖口按二次贝塞尔曲线到终点 for (var i = 0; i < 4; i++) { //起点坐标,盖口,每个水滴往下移一点,免点重叠 var start = { x: this.capData.startPos.x + this.capData.startPos.x * 0.05, y: this.capData.startPos.y + this.capData.startPos.y * (i + 1) * 0.15 }; //终点坐标,画面右下角,每个水滴往下移一点,免点重叠 var end = { x: this.initData.rendSize.width, y: start.y + start.y * 0.5 + i * (start.y * 0.1) }; //获取二次贝塞尔曲线控制点 var control = this.getSecControl(start.x, start.y, end.x, end.y); //水滴的水平角度依次加5度,直线公式:y=5x-5; var angel = 5 * i - 5; //获取当前要画的位置 var point = this.getCurpoint(start, end, control, rate); //画水滴 this.drawCur( this.waterData.ctx, 60, angel, 15 + 0.15 * rate * 100, point.x, point.y ); } this.waterData.ctx.closePath(); this.waterData.ctx.fill(); }, /* 根据贝塞尔公式获取在指定贝塞尔曲线上的点 start:曲线起点 end:曲线终点 c:曲线控制点 t:控制变量,为0时是起点,为1时是终点 */ getCurpoint(start, end, c, t) { var x = (1 - t) * (1 - t) * start.x + 2 * t * (1 - t) * c.x + t * t * end.x; var y = (1 - t) * (1 - t) * start.y + 2 * t * (1 - t) * c.y + t * t * end.y; return { x: x, y: y }; }, /* 获取二次贝塞尔曲线的控制点,X坐票取中点,Y坐标取起点Y坐标稍下。 startx:起点x坐标 starty:起点y坐标 endx:终点x坐标 endy:终点y坐标 */ getSecControl(startx, starty, endx, endy) { return { x: startx + (endx - startx) / 2, y: starty - endy * 0.1 }; }, /* 画水滴,由二条三次贝塞尔曲线组成 ctx:Canvas Context angel:夹角大小(水滴大小) hangel:水平角度(水滴方向) line:从起点到水滴终点的距离长度(像素))))),决定了水滴长度 startx:起点x坐标 starty:起点y坐标 */ drawCur(ctx, angel, hangle, line, startx, starty) { angel = angel * Math.PI / 180; hangle = hangle * Math.PI / 180; var p1 = []; var p2 = []; p1[0] = Math.cos(hangle) * line + startx; p1[1] = Math.sin(hangle) * line + starty; p2[0] = startx + Math.cos(angel + hangle) * line; p2[1] = starty + Math.sin(angel + hangle) * line; ctx.moveTo(startx, starty); var p = { p1: p1, p2: p2 }; var c1 = [(startx + p.p1[0]) / 2, (starty + p.p1[1]) / 2]; var c2 = [(p.p1[0] + p.p2[0]) / 2, (p.p1[1] + p.p2[1]) / 2]; ctx.bezierCurveTo(c1[0], c1[1], p.p1[0], p.p1[1], c2[0], c2[1]); var c3 = [(p.p2[0] + startx) / 2, (p.p2[1] + starty) / 2]; ctx.bezierCurveTo(p.p2[0], p.p2[1], c3[0], c3[1], startx, starty); ctx.stroke(); } } }; </script>
标签:tpi function div arm 长度 min assets ext eth
原文地址:https://www.cnblogs.com/liujiabing/p/11582762.html