zoukankan      html  css  js  c++  java
  • 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="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,
             "100%",
            height: "100%"
          },
          /**画布初始化参数 */
          initData: {
            /**原图尺寸 */
            size: {
               340,
              height: 380
            },
            /**渲染尺寸 */
            rendSize: {
               0,
              height: 0
            }
          },
          /**底部墙渲染数据 */
          wallData: {
            //画片
            pic: require("@/assets/img/hr/ic_di.png"),
            //canvas上下文
            ctx: null,
            //原图尺寸
            size: {
               340,
              height: 166
            },
            //渲染尺寸
            rendSize: {
               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: {
               127,
              height: 216
            },
            //渲染尺寸
            rendSize: {
               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: {
               20,
              height: 46
            },
            //渲染尺寸
            rendSize: {
               0,
              height: 0
            },
            //起点坐标
            startPos: {
              x: 0,
              y: 0
            }
          },
          //水滴渲染数据
          waterData: {
            ctx: null,//canvas上下文
            //渲染尺寸
            rendSize: {
               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>
    

      

  • 相关阅读:
    MyEclipse 6.0.1 注册码过期后的解决办法
    .NET代码管理工具 Narrange
    oracle 测试代码
    log4net for web快速入门
    Sql 2005 千万级数据库IO规划
    VSS Internet Access Configuration [转贴]
    开车二十年后得到的真实的26条教训!开车的人一定看看!&not;
    在 IIS 6.0 中以编程方式管理服务器证书
    ASP.NET页面刷新的实现方法(Zt)
    服务器处理器一览
  • 原文地址:https://www.cnblogs.com/liujiabing/p/11582762.html
Copyright © 2011-2022 走看看