zoukankan      html  css  js  c++  java
  • 使用vue+zrender绘制体温单 三测单(2)

    预览地址 http://106.12.212.110:8077/#/

    上期我们说了如何创建项目并把各个项目的文件结构创建好后这期我们来说如何画出图中代写线段

    首先我们在src/components/Render.vue中添加一下引用

    import zrender from 'zrender'
    import {chartData,configData} from '@/mock/index' 这是mock数据
    import { createLine,createCircle,addHover,createPolygon,hoverLine } from '../js/utli' 这是负责创建线段 阴影图等 公共方法 该文件在文章底部
    import {mapState,mapGetters,mapMutations,mapActions} from 'vuex' 这是vuex负责存储数据

    然后我们在data里写入以下属性

    //线段开始横坐标
          lineStartX:0,
          //线段开始纵坐标
          lineStartY:0,
          //线段结束横坐标
          lineEndX:0,
          //线段结束纵坐标
          LineEndY:0,
          //多少个y轴坐标
          xLineLen:{
            //天数 7天 
            day:0,
            //一天多少分段
            time:6
          },
          canavsWidth:0, //画板宽度
          canavsHeight:0, //画板高度
          zr:"", //画板属性
          yLineLen:{
            XRegion:13, //X轴坐标分几个大块
            XShare:5, //每块份几个小块
            XLineArr:[3], //需要特殊处理的横线 冲上往下算
          },
          YLineReset:[], //Y轴每一小格份几份
          0,
          YCellHeight:0, //y轴大格子高度
          lastData:0,  //上一个数据
          CircleSize:8, //画板上圆点的直径
          hoverCircleSize:10,//画板上圆点移入变化的直径
          fontSize:15, //画板上圆圈里的字体大小 

    这些属性都是以数据的形式来创建画板的横坐标 竖坐标 横线 竖线  基础属性创建好后我们在methods里先创建一个init方法 该方法负责初始化创建一个cavans画板 以及获取一些页面基础属性

    init(){
          this.zr = zrender.init(document.getElementById("main"))
          var div = document.createElement("div")
          div.classList.add("tips")
          document.getElementById("main").append(div)
          
          this.canavsWidth = this.zr.getWidth()
          this.canavsHeight = this.zr.getHeight()
          
          if (this.httpType == 'http') {
             this.$axios({
                method:'post',
                url:`${this.configUrl}/api/PatrolInfo/ChartData`,
                data:{"PatientCode":this.urlData['cstId'],"beginDate":this.urlData['begin'],"endDate":this.urlData['end'],"PatroInfoType":this.urlData['PatroInfoType']},
              }).then(res => {
                  res = res.data.Data
                  this.xLineLen.time = this.TimeArr.length
                  this.YLineReset = this.resetY(this.TimeArr)
                  this.filterData(res)
                  this.yLine() //生成Y轴坐标
                  this.xLine() //生成X轴坐标
                  this.getCellHeight()
              })
          }else{
            this.xLineLen.time = this.TimeArr.length
            this.xLineLen.day = 7
            this.YLineReset = this.resetY(this.TimeArr)
            this.filterData(chartData)
            this.yLine() //生成Y轴坐标
            this.xLine() //生成X轴坐标
            this.getCellHeight()
          }
          // this.hoverLine()
        },

    这里使用了一个判断 来判断当前是本地版本还是线上版本

    这里的httpType通过src/store/http.js来配置是本地还是线上 没有文件请先创建

    http.js  httpType是mock就是本地 http就是线上 

    import Vue from 'vue'
    import Vuex from 'vuex'
    import axios from 'axios'
    
    Vue.use(Vuex)
    
    const store = new Vuex.Store({
        state:{
            configUrl:'', //先自己的请求地址
            httpType:"mock",
            data:JSON.parse(localStorage.getItem('patientData')), //此方法可用可不用  用于基础普通html项目是使用
        },
        mutations:{
          
        },
        actions:{
            getUrlData(context){
                context.commit('setUrlData',data)
            }
        }
    })
    
    export default store

    init方法创建好后里面有4个方法分别是

    this.filterData(res) 过滤数据
    this.yLine() //生成Y轴坐标
    this.xLine() //生成X轴坐标
    this.getCellHeight() 获取格子的总高度
    yLine() {
          //横坐标 最底部横坐标
          let Xline = new zrender.Line({
            shape:{
              x1:0,
              y1:this.canavsHeight,
              x2:this.canavsWidth,
              y2:this.canavsHeight
            }
          })
          this.zr.add(Xline)
          const yWidth = this.canavsWidth/this.xLineLen.day
          
          //循环显示竖线格子 红色竖线
          for (let i = 0; i < this.xLineLen.day; i++) {
             //纵坐标
            let Yline = new zrender.Line({
              shape:{
                x1:yWidth*i,
                y1:0,
                x2:yWidth*i,
                y2:this.canavsHeight
              },
              style:{
                opacity:1,
                lineWidth:1,
                stroke:"#ff0000"
              }
            })
            this.zr.add(Yline)
          }
    
          let yLinAll = this.xLineLen.day*this.xLineLen.time
          for (let i = 0; i < yLinAll; i++) {
             let Yline = new zrender.Line({
              shape:{
                x1:yWidth/this.xLineLen.time*i,
                y1:0,
                x2:yWidth/this.xLineLen.time*i,
                y2:this.canavsHeight
              },
              style:{
                opacity:1,
                lineWidth:0.5,
                stroke:"#000"
              }
            })
       
            this.zr.add(Yline)
          }
        },
        xLine(){
          let xHeight = this.canavsHeight/this.yLineLen.XRegion
          let XShareAll = this.yLineLen.XRegion*this.yLineLen.XShare
          for (let i = 0; i < this.yLineLen.XRegion; i++) {
            let color = "#000"
            this.yLineLen.XLineArr.forEach(el => {
              if (el == i) {
                color = "#ff0000"
              }
            });
            //横坐标 加粗
            let Xline = new zrender.Line({
              shape:{
                x1:0,
                y1:xHeight*i,
                x2:this.canavsWidth,
                y2:xHeight*i
              },
              style:{
                opacity:1,
                lineWidth:2,
                stroke:color
              }
            })
            this.zr.add(Xline)
    
            for (let a = 0; a < XShareAll; a++) {
              //横坐标
              let Xline = new zrender.Line({
                shape:{
                  x1:0,
                  y1:xHeight/this.yLineLen.XShare*a,
                  x2:this.canavsWidth,
                  y2:xHeight/this.yLineLen.XShare*a
                },
                style:{
                  opacity:1,
                  lineWidth:0.4,
                  stroke:"#000"
                }
              })
              this.zr.add(Xline)
            }
          }
        },
        filterData(data){
          //重置信息 避免重复出现的bug
          this.lastData = 0
          data.forEach((el,i) => {
            switch (el.type) {
              case "text":
                this.zrText(el)
                break;
              case "line":
                this.zrLine(el)
                break;
              case "area":
                this.zrPolyline(el)
                break;
              case "tag":
                this.zrTag(el)
                break;
            
              default:
                break;
            }
            });
        },

    横纵坐标创建好后我们就开始创建折线 阴影等图

    //绘制文本内容
        zrText(data){
          if (this.xLineLen.day*24 >= data.time) {
            //最小值
            const cellMin = data.cellMin
            //坐标轴每格代表值
            const cellSplit = data.cellSplit
            var textWidthHeight = 15 //一个字的原始宽度高度
            var textHeight = 0 //字体总高度
            var textWidth = textWidthHeight //字体总宽度
            var moveRange = textWidthHeight/2 //需要移动的距离 用于移动字体下面的矩形背景框
            //计算文本高度
            if (data.text) {
              let dataLen = data.text.split("
    ").length
              //需要换行的字体 移动距离是字体的一半 每个字宽度,高度为12
              if (dataLen > 1) {
                textHeight = dataLen * textWidthHeight
                textWidth = textWidthHeight
              } else {
                let textLen = data.text.length
                textHeight = textWidthHeight
                textWidth = textWidthHeight * textLen
                moveRange = textWidthHeight
              }
            }
            let xWidth = this.XShareOne(data.time)
            
            //如果和上一个时间相同就往后移动
            if ( this.lastData != 0 || data.time <= 1 ) {
              if (this.lastData == data.time) {
                xWidth = xWidth + textWidth + 5
              }
            }
            this.lastData = data.time  //存入当前时间 用于重合区分
            let YHeight = this.YShareOne().height
            let y = this.transformY(data.position,cellSplit,cellMin)
            let state = new zrender.Group();
            state.add(
              new zrender.Rect({
                shape:{
                  x:xWidth-(textWidth/2),
                  y:y,
                  textWidth,
                  height:textHeight
                },
                style:{
                  fill:"#FFF"
                },
                zlevel:4
              })
            )
            state.add(
              new zrender.Text({
                style:{
                  text:data.text,
                  textShadowColor:"#fff",
                  textStroke:"#fff",
                  textFill:data.color,
                  textAlign:"center",
                  fontSize:13
                },
                position:[xWidth,y],
                zlevel:4
              })
            );
            this.zr.add(state)
          }
        },
    zrLine(data){
          
          var style = {}
          //最小值
          const cellMin = data.cellMin
          //坐标轴每格代表值
          const cellSplit = data.cellSplit
                
          data.array.forEach((el,i) =>{
            //过滤shape 个别需特殊处理 后期需优化
            switch (el.shape) {
              case "x-circle":
                style = {
                  stroke:data.color,
                  fill:"#fff",
                  text:"x",
                  fontSize:this.fontSize
                }
                break;
              case "empty-circle":
                style = {
                  stroke:data.color,
                  fill:"#fff",
                  text:"",
                }
                break;
              case 'x':
                style = {
                  stroke:data.color,
                  fill:"#fff",
                  text:"x",
                  fontSize:this.fontSize
                }
                break;
              case 'o-circle':
                style = {
                  stroke:data.color,
                  fill:"#fff",
                  text:"●",
                  fontSize:this.fontSize
                }
                break;
              case '':
                style = {
                  stroke:data.color,
                  fill:data.color,
                  text:"",
                }
                break;
              default:
                break;
            }
            //疼痛单独处理
            if (el.type == "pain") {
               style = {
                  stroke:data.color,
                  fill:"#fff",
                  text:"",
                }
            }
            
            if (i > 0) {
              let firstX = this.getX(data.array[i-1].time)  
              let firstY = this.transformY(data.array[i-1].value,cellSplit,cellMin)
    
              let x = this.getX(data.array[i].time)
              let y = this.transformY(data.array[i].value,cellSplit,cellMin)
              
              if (data.array[i-1].Break == "false") {
                let line = createLine(firstX,firstY,x,y,{
                    stroke:data.color,
                    lineWidth:2,
                })
                this.zr.add(line)
              }
            }
            
            if (el.extraArr && el.extraArr.length > 0) {
                el.extraArr.forEach((item,a) => {
                  console.log(item);
                  
                  let x = this.getX(el.time)
                  let y = this.transformY(el.value,cellSplit,cellMin)
    
                  let lastY =  this.transformY(item.extra,cellSplit,cellMin)
                  let dottedLine = createLine(x,y,x,lastY,{
                      stroke:data.color,
                      lineWidth:3,
                      lineDash:[2,2]
                  })
                  this.zr.add(dottedLine)
    
                  el.extraArr.forEach((item,a) => {
                    let getY = this.transformY(item.extra,cellSplit,cellMin)
                    
                    let Circle = createCircle(x,getY,this.CircleSize,{
                      stroke:item.extraColor,
                      fill:"#fff",
                    })
                    this.zr.add(Circle)
                    addHover(Circle,{
                        tips:item.extraTips,
                    },x,getY,{
                        r:this.hoverCircleSize,
                      },{
                        r:this.CircleSize,
                    })
                  })
                })
             }
            let getX = this.getX(el.time)
            let getY = this.transformY(el.value,cellSplit,cellMin)
    
            let Circle = createCircle(getX,getY,this.CircleSize,style)
            this.zr.add(Circle)
            addHover(Circle,el,getX,getY,{
                r:this.hoverCircleSize,
              },{
                 r:this.CircleSize,
            })
          })
        },
        //多边形
        zrPolyline(data){
          console.log(data);
          
          //最小值
          const cellMin = data.cellMin
          //坐标轴每格代表值
          const cellSplit = data.cellSplit
          var points = []
          data.array.forEach((el,i) => {
            //生成圆点
            let cx = this.getX(el.time)
            let cy1 = this.transformY(el.v1,cellSplit,cellMin)
            let Circle1 = createCircle(cx,cy1,this.CircleSize,{
                stroke:data.color,
                fill:"#fff",
                text:"",
              })
            this.zr.add(Circle1)
            addHover(Circle1,{tips:el.v1Tips},cx,cy1,{
              r:this.hoverCircleSize,
            },{
                r:this.CircleSize,
            })
    
            let cy2 = this.transformY(el.v2,cellSplit,cellMin)
            let Circle2 = createCircle(cx,cy2,this.CircleSize,{
                stroke:data.color,
                fill:data.color,
                text:"",
              })
            this.zr.add(Circle2)
            
            addHover(Circle2,{tips:el.v2Tips},cx,cy2,{
              r:this.hoverCircleSize,
            },{
                r:this.CircleSize,
            })
    
            if (i > 0) {
              if (data.array[i-1].Break == "false") {
                points = []
                let pox1 = this.getX(data.array[i-1].time)
                let poy1 = this.transformY(data.array[i-1].v1,cellSplit,cellMin)
                let poy2 = this.transformY(data.array[i-1].v2,cellSplit,cellMin)
                
                let pox3 = this.getX(el.time)
                let poy3 = this.transformY(el.v1,cellSplit,cellMin)
                let poy4 = this.transformY(el.v2,cellSplit,cellMin)
                
                points.push([pox1,poy1],[pox1,poy2],[pox3,poy4],[pox3,poy3],[pox1,poy1])
                
                let area = createPolygon(points,{
                  fill:data.bgColor,
                  opacity:0.8,
                  stroke:data.color
                })
    
                this.zr.add(area)
              }
            }
          })
          
        },
        zrTag(data){
          //最小值
          const cellMin = data.cellMin
          //坐标轴每格代表值
          const cellSplit = data.cellSplit
          if (data.text == "R") {
            data.array.forEach((el,i) => {
              let x = this.getX(el.time)
              let y = this.transformY(el.value,cellSplit,cellMin)
    
              let Circle = createCircle(x,y,this.CircleSize,{
                text:data.text,
                fill:"#fff",
                stroke:data.color,
                textVerticalAlign:"middle",
                textAlign:"center",
              })
              this.zr.add(Circle)
              addHover(Circle,{tips:""},0,0,{
                r:this.hoverCircleSize,
              },{
                r:this.CircleSize,
              })
            })
          }
          if(data.text == "H"){
            data.array.forEach((el,i) => {
              let x = this.getX(el.time)
              let y = this.transformY(el.y,cellSplit,cellMin)
    
              let Circle = createCircle(x,y,this.CircleSize,{
                text:data.text,
                fill:"#fff",
                stroke:data.color,
                textVerticalAlign:"middle",
                textAlign:"center"
              })
              this.zr.add(Circle)
              addHover(Circle,{tips:""},0,0,{
                r:this.hoverCircleSize,
              },{
                r:this.CircleSize,
              })
            })
          }
        },

    创建图形是还需要写几个方法

    用于获取x轴小格子的宽度

    y轴小格子宽度

    以及每日时间变化后每个坐标点的定位

     //每个x轴小格子宽度是多少
        XShareOne(data){
          let widthArr = [] //每格宽度 全部存入数组
          var width = 0
          let YLineResetAll = []  //7天所有份数
          
          for (let i = 0; i < 7; i++) {
            YLineResetAll.push(...this.YLineReset)
          }
          
          for (let i = 0; i < parseInt(data); i++) {
              width = YLineResetAll[i].width + width
          }
            
          return width
        },
        //每个Y轴小格子宽度是多少
        YShareOne(){
          //计算大格子里的每格小格子高度
          let childerHeight = this.canavsHeight/this.yLineLen.XRegion/this.yLineLen.XShare
          //计算大格高度
          let height = this.canavsHeight/this.yLineLen.XRegion
          return {height:height,childerHeight:childerHeight}
        },
        //转换y轴坐标点为正确坐标点 因为y轴坐标是顶点为0递增的 所有用总高度减去原来坐标的高度剩下的高度就是正确坐标点
        //i代表一个格子代表几个高度
        transformY(data,i,cellMin){
          let YHeight = this.YShareOne().height
          let YHeightChilder = this.YShareOne().childerHeight
          let xAll = this.yLineLen.XRegion  //一共多少个横坐标 大的
          let surplusHeight
          var cellAll = this.yLineLen.XRegion*this.yLineLen.XShare //一共多少个横坐标
          let index = cellMin
          var aIndex = 0
          let lastNumber = 0
          //总共占几格
          for (let a = 0; a < cellAll; a++) {
            //每格代表的值小于0的时候 需要特殊处理
            if (parseInt(i) == 0) {
              let floatNumber = this.getFloat(index,1)
              if (floatNumber <= this.getFloat(data,1)) {
                lastNumber = floatNumber
                aIndex = a
                surplusHeight = this.canavsHeight -this.getFloat(YHeightChilder,1)*a
              }
            }else{
              if (index <= data) {
                lastNumber = index
                aIndex = a
                surplusHeight = this.canavsHeight - YHeightChilder*a
              }
            }
            index = index+i
          }
          
          if (lastNumber-data < 0) {
            surplusHeight = surplusHeight -YHeightChilder/2
          }
    
          return surplusHeight
        },
    transformY(data,i,cellMin)这个方法里的逻辑比较复杂 后面我们在单独讲
    下面是几个基础方法
    resetY方法也是一个重点后面再讲
     
        //获取X坐标 data当前时间点
        getX(data){
          let XShareOne = this.XShareOne(data)
          return XShareOne
        },
        //重置y轴坐标间隔条数 传入日期数组 格式["1","3","4","5","6","18","21","24"]
        resetY(data){
          let oneYLinWidth = this.canavsWidth/this.xLineLen.day/this.xLineLen.time //每个时间点格子宽度
          let resetArr = [] //得到的新数组
          
          data.forEach((item,i) => {
            if (i == 0) {
              for (let index = 0; index < item; index++) {
                resetArr.push({
                  oneYLinWidth/2/item
                })
              }
            }else{
                let indexItem = item - data[i-1]
                for (let index = 0; index < indexItem; index++) {
                  resetArr.push({
                    oneYLinWidth/indexItem
                  })
                }
               if (i+1 == data.length) {
                 let indexItem = 24 - item
                 for (let index = 0; index < indexItem; index++) {
                    resetArr.push({
                    oneYLinWidth/2/indexItem
                  })
                 }
               }
            }
          })
          return resetArr
        },
        getFloat(num,n){
          n = n ? parseInt(n) : 0;
          if(n <= 0) {
              return Math.round(num);
          }
          num = Math.round(num * Math.pow(10, n)) / Math.pow(10, n); //四舍五入
          num = Number(num).toFixed(n); //补足位数
          return num;
        },
        getCellHeight(){
          //yHeight Y轴每个小格子高度 xHeight X轴每个小格子宽度
          let xWidth = this.canavsWidth / this.xLineLen.day / this.xLineLen.time
          this.$emit('yHeight',this.YShareOne().height)
          this.$emit('xHeight',xWidth)
        },
        hoverLine(){
          var timer = null;
          let line = new zrender.Line({
              shape:{
                  x1:0,
                  y1:0,
                  x2:0,
                  y2:this.canavsHeight
              },
          })
          this.zr.add(line)
          
          hoverLine(this.zr,line,this.canavsHeight)
        }
    <template>
      <div id="main">
      </div>
    </template>

    这是创建初始cavans需要用的

    <style scoped>
      #main{
        height: 1250px;
         100%;
        position: relative;
      }
      html,body{
        height: 100%;
         100%;
        margin: 0;
        padding: 0;
      }
      canvas{
         100%;
        height: 700px;
      }
    </style>

    然后是src/js/utli.js里的各个方法

    import zrender from "zrender"
    import moment from 'moment';
    
    //线段
    export const createLine = (x1,y1,x2,y2,style)=>{
        return new zrender.Line({
            shape:{
                x1:x1,
                y1:y1,
                x2:x2,
                y2:y2
            },
            style:style,
        });
    };
    // cx 横坐标 cy纵坐标 r半径 空心圆
    export const createCircle = (cx,cy,r,style)=>{
        return new zrender.Circle({
            shape:{
                cx:cx,
                cy:cy,
                r:r
            },
            style:style,
            zlevel:4
        })
    }
    //添加horver事件 el 元素对象 config 一些配置项 x x轴坐标 y y轴坐标 shapeOn鼠标移入一些属性配置 shapeOn鼠标移出一些属性配置 shape配置项看官网  
    export const addHover = (el,config,x,y,shapeOn,shapeOut) => {
        const domTips = document.getElementsByClassName("tips")
        el.on('mouseover',function(){
            domTips[0].innerHTML = config.tips
            
            let textWidth = config.tips.length*15
            domTips[0].setAttribute("style",`position:absolute;top:${y-30}px;left:${x-textWidth/2}px;display:block;font-size:10px;background-color:rgba(0,0,0,.7);padding:3px 2px;border-radius:2px;color:#fff;${textWidth}px;text-align:center`)
            el.animateTo({
                shape:shapeOn
            },100,0)
        }).on('mouseout',function () {
            domTips[0].setAttribute("style",`display:none`)
            el.animateTo({
                shape:shapeOut
              },100,0)
        })
    }
    //多边形
    export const createPolygon = (points,style) => {
        return new zrender.Polyline({
            shape:{
                points:points,
            },
            style:style
        })
    }
    
    export const hoverLine = (el,line,y2) => {
        window.onmousemove = function (e) {
            line.animateTo({
                shape:{
                    x1:e.offsetX,
                    y1:0,
                    x2:e.offsetX,
                    y2:y2
                }
            },50,0)
        }
    }
    //时间格式化
    export const getFullTime = (i) => {
        return moment(i).format("YYYY-MM-DD HH:mm:ss");
    }
    //贝塞尔曲线
    export const BezierCurve = (x1,y1,x2,y2,cpx1,cpy1,style) => {
        return new zrender.BezierCurve({
            shape:{
                x1:x1,
                y1:y1,
                x2:x2,
                y2:y2,
                cpx1:cpx1,
                cpy1:cpy1
            },
            style:style,
        });
    }

    这里创建完成后画板区域基本完成加上数据就能看见效果了

    关注公众号回复 体温单 获取源代码

     
  • 相关阅读:
    STM32中断优先级理解
    STM32按键控制程序
    STM32的LED驱动程序
    嵌入式程序员应知道的0x10个C语言Tips[转]
    【Unity】使用RenderTexture为物体生成快照
    对装饰模式(Decorator)的解读
    设计模式之初:理解面向对象设计
    windows RT系统下解决网络丢包问题
    IOS推出测试平台
    小米路由试用心得3——关于数据备份及客户端软件
  • 原文地址:https://www.cnblogs.com/hprBlog/p/13375696.html
Copyright © 2011-2022 走看看