Javascript实现的智能消防栓状态监测画面
系统需要添加智能消防栓模块。集成了一家采用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>
Javascript实现的智能消防栓状态监测画面的更多相关文章
- intouch与PLC之间通讯状态监测和设置
intouch与PLC进行通讯状态监测中,一般做法需要PLC来实施主动脉冲计数,或者bool变化来实现.本文通过上位机自带参数设置,实现对intouch通讯状态监视,将画面恢复初始状态,并及时弹窗报警 ...
- SharePoint 2013 使用JavaScript对象模型配置智能提示
前言 默认在VS2012/2013中编写SharePoint JavaScript 客户端对象模型,都没有智能感知的功能,用起来非常麻烦:其实,我们可以手动配置一下,让JavaScript可以进行智能 ...
- 用 JavaScript 检测浏览器在线/离线状态(JavaScript API — navigator.onLine)
如今HTML5 移动应用或 Web app 中越来越普遍的使用了离线浏览技术,所以用 JavaScript 检测浏览器在线/离线状态非常常见. 无论浏览器是否在线,navigator.onLine 属 ...
- 【转载】 GPU状态监测 nvidia-smi 命令详解
原文地址: https://blog.csdn.net/huangfei711/article/details/79230446 ----------------------------------- ...
- Tomcat 项目部署、账户配置、状态监测
tomcat部署项目 方式一.自动部署(最常用) 直接把war包或部署的文件夹放到webapps下. tomcat启动后会自动监听webapps下的文件|目录,放入打包好的项目会自动部署,移除打包好的 ...
- How Javascript works (Javascript工作原理) (十) 使用 MutationObserver 监测 DOM 变化
个人总结: 这篇文章介绍了几种监测DOM变化的方法,重点介绍的是一个新浏览器API叫做MutationObserver. 注意:不要和Vue.js种 Object.defineProperty() 的 ...
- Javascript设计模式之我见:状态模式
大家好!本文介绍状态模式及其在Javascript中的应用. 模式介绍 定义 当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式主要解决的是控制一个对象状态的条件表达式 ...
- JavaScript学习--(智能社视频)
JavaScript学习,这是智能社官网上的JS视频,讲解的挺不错的,就是没有视频的demo,在视频中附上自己编写的一些demo... 下载地址:http://pan.baidu.com/s/1cPz ...
- JavaScript中的设计模式:状态模式
前几天写了一个贪吃蛇小游戏,正好用到了状态模式. 定义 当一个对象内部状态发生改变时候,会导致其行为的改变,这看起来像是改变了对象. 简单的例子 如果一个函数要更具某一个对象的状态来判断该对象应该执行 ...
随机推荐
- 微信小程序 movable-view组件应用:可拖动悬浮框_返回首页
1. movable-view组件具体内容可参考官网:微信官方文档 2. demo参考:https://github.com/ChinaFanny/YFWeappMovableView 运行效果 核心 ...
- node.js使用cluster实现多进程
首先郑重声明: nodeJS 是一门单线程!异步!非阻塞语言! nodeJS 是一门单线程!异步!非阻塞语言! nodeJS 是一门单线程!异步!非阻塞语言! 重要的事情说3遍. 因为nodeJS天生 ...
- 第10组Alpha冲刺(1/4)
队名:凹凸曼 组长博客 作业博客 组员实践情况 童景霖 过去两天完成了哪些任务 文字/口头描述 学习Android studio和Java,基本了解APP前端的制作 完善项目APP原型 展示GitHu ...
- mocha单元测试简易教程
mocha单元测试简易教程 写在前面 其实mocha单元测试的教程网上有很多,也都很简单易懂,但是每个人对同一份的教程也会产生不同的理解,像我这种大概就是走遍了所有弯路才到达终点的人,想通过给大家分享 ...
- 定制比特币btc地址address
https://99bitcoins.com/how-to-get-a-custom-bitcoin-address/ https://en.bitcoin.it/wiki/Vanitygen htt ...
- 第2课第5节_Java面向对象编程_异常_P【学习笔记】
摘要:韦东山android视频学习笔记 java的异常处理的原则如下: 1.我们先写一个没有对异常处理的程序,在进行除法运算的时候,除数是非零的话,运行时没有问题的,但是除数为零的时候,运行就会有问 ...
- 【mybatis源码学习】mybatis的插件功能
一.mybatis的插件功能可拦截的目标 org.apache.ibatis.executor.parameter.ParameterHandler org.apache.ibatis.executo ...
- ORA-55624: 此时无法为闪回归档启用表
我们在某应用中使用了FDA特性,但是某些表在解除归档后重新启用时报"ORA-55624: 此时无法为闪回归档启用表",经查询google和MOS相关信息,原因就是太频繁.解决方法: ...
- Java与.net 关于URL Encode 的区别
在c#中,HttpUtility.UrlEncode("www+mzwu+com")编码结果为www%2bmzwu%2bcom,在和Java开发的平台做对接的时候,对方用用url编 ...
- Java基础 try...catch 处理ArithmeticException 除以零的异常
JDK :OpenJDK-11 OS :CentOS 7.6.1810 IDE :Eclipse 2019‑03 typesetting :Markdown code ...