Javascript实现的智能消防栓状态监测画面

时间:2019-09-25
本文章向大家介绍Javascript实现的智能消防栓状态监测画面,主要包括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>

  

原文地址:https://www.cnblogs.com/liujiabing/p/11582762.html