大卫三角形法(canvas)和立体图示法(echarts)实现

气体比值的图示法

 实现

需求


 

1)大卫三角形为canvas画出图片,然后计算出点的位置画出红点。

2)立体图示法为根据需求提供的公式及数据通过echarts实现,难点在对数x、y、z轴无法生成立体柱状图,解决方案为生成了两个echarts图表,一个为只显示立体柱状图(实际为正常坐标系,但将坐标系隐藏),一个为只显示对数坐标系(提供展示效果),然后计算出正常坐标系0-10对于对数坐标系0-0.1-1-10 对应的位置。

父组件代码

<template>
  <div class="imgContent">
    <div class="imgItem">
      <div class="titleText">
        <p>立体图示法</p>
        <p v-if="echartsValueObj" class="valueText" >C<sub>2</sub>H<sub>2</sub>/C<sub>2</sub>H<sub>4</sub>:{{echartsValueObj['C2H2/C2H4']}} &nbsp;&nbsp; C<sub>2</sub>H<sub>4</sub>/C<sub>2</sub>H<sub>6</sub>:{{echartsValueObj['C2H4/C2H6']}} &nbsp;&nbsp; CH<sub>4</sub>/H<sub>2</sub>: {{echartsValueObj['CH4/H2']}}</p>
      </div>
      <stereographicRepresentation :dataList="dataList" @returnValue="getBackValue"></stereographicRepresentation>
    </div>
    <div class="imgItem" >
      <div class="titleText">
        <p>大卫三角形法</p>
        <p class="valueText" >%C<sub>2</sub>H<sub>2</sub>:  {{C2H2_percent.toFixed(2)}}% &nbsp;&nbsp; %CH<sub>4</sub>:  {{ CH4_percent.toFixed(2) }}% &nbsp;&nbsp; %C<sub>2</sub>H<sub>4</sub>: {{ C2H4_percent.toFixed(2) }}%&nbsp;&nbsp; </p>

      </div>
      <canvas ref="davCanvas" width="428" height="400"></canvas>
    </div>

    <div class="bottomText">
      <p>PD一局部放电:Dl—低能放电:D2高能放电;Tl—热故障,t<300℃
        T2—热故障,300℃<t<700℃;T3—热故障,t>700℃</p>
      <p>说明:当计算结果在气体比值的极限之外时,三比值法或溶解气体解释表给不出诊断结果,运行人员可使用图示法辅助判断。
        观察立体图示法中最接近未诊断情况的区域,容易直观地注意到当前告警情况的变化趋势,此时大卫三角形法总能提供一种诊断结果。</p>

    </div>
  </div>
</template>

<script>
import stereographicRepresentation from "@/components/stereographicRepresentation/index.vue";
export default {
  name: "triangleOfDavid",
  components:{stereographicRepresentation},
  data() {
    return {
      valueObjs: {},
      dataList:null,
      echartsValueObj:null,
      C2H2_percent:0,
      C2H4_percent:0,
      CH4_percent:0,
    };
  },
  mounted() {},
  methods: {
    getBackValue(value) {
      console.log('value',value)
      this.echartsValueObj = value;
    },
    initCanvas(dataList) {
      this.dataList = dataList
      let valueObjs = {};
      for (let item of dataList.pointResultDataVOList) {
        valueObjs[item.gasName] = Number(item.value);
      }
      for (let item of dataList.incrementList) {
        valueObjs[item.gasName] = Number(item.value);
      }
      this.valueObjs = valueObjs;
      let position_percent = this.calculateDavidsonTrianglePosition(
        valueObjs.C2H2,
        valueObjs.C2H4,
        valueObjs.CH4
      );
      //计算出真实px的坐标
      let position_px = this.calculateTrianglePosition(
        position_percent.x,
        position_percent.y,
        38,
        44,
        214,
        400
      );
      //因为canvas是以左上角为原点,数据以中心点为原点,需要换算为以左上角为原点对应的坐标
      let position_x = 214 + position_px.x;
      let position_y = 220 - position_px.y;
      //解析数据
      const ratioNameRatioValuePairs = dataList.pointResultDataVOList.reduce(
        (acc, curr) => {
          acc[curr.ratioName] = curr.ratioValue;
          return acc;
        },
        {}
      );
      const img = new Image();
      const davcanvas = this.$refs.davCanvas;
      let ctx = null;
      // 图片加载完成后执行绘制操作
      // 设置图片的源
      img.src = require("@/assets/images/davidWhite.png"); // 替换为你的 PNG 图片路径
      img.onload = () => {
        // 获取 Canvas 元素
        ctx = davcanvas.getContext("2d");
        ctx.drawImage(img, 0, 0, davcanvas.width, davcanvas.height);
        ctx.beginPath();
        ctx.arc(position_x, position_y, 4, 0, 2 * Math.PI); // 使用 arc 方法绘制一个圆形
        ctx.fillStyle = "red"; // 设置填充颜色
        ctx.fill(); // 填充圆形
      };
    },

    calculateDavidsonTrianglePosition(C2H2_amount, C2H4_amount, CH4_amount) {
      // 计算总量
      const total = C2H2_amount + C2H4_amount + CH4_amount;

      // 计算百分比
      const C2H2_percent = (C2H2_amount / total) * 100;
      const C2H4_percent = (C2H4_amount / total) * 100;
      const CH4_percent = (CH4_amount / total) * 100;
      this.C2H2_percent = C2H2_percent;
      this.C2H4_percent = C2H4_percent;
      this.CH4_percent = CH4_percent;
      // 计算混合物点的位置
      const x = C2H2_percent / 100;
      const y = C2H4_percent / 100;
      const z = CH4_percent / 100;

      const xPosition = (100 * x) / (x + y + z);
      const yPosition = (100 * y) / (x + y + z);
      return { x: xPosition, y: yPosition };
    },
    //计算去除留白后的百分比坐标对应的px坐标
    calculateTrianglePosition(
      x_percent,
      y_percent,
      topMargin,
      bottomMargin,
      triangleWidth,
      triangleHeight
    ) {
      // 计算实际三角形高度
      const actualTriangleHeight = (triangleHeight - topMargin) / 2;

      // 计算相对于三角形的位置
      const x_position = (x_percent / 100) * triangleWidth;
      const y_position = (y_percent / 100) * actualTriangleHeight;

      return { x: x_position, y: y_position };
    },
  },
};
</script>

<style scoped>
.imgContent {
  width: 90%;
  margin: 0 auto;
  height:700px;
  text-align: center;
}
.bottomText{
  margin: 0 auto;
  width: 70%;
  text-align: center;
}
.imgItem{
  position: relative;
  width: 50%;
  height: 600px;
  display: inline-block;
  vertical-align: middle;
  .titleText{
    position: absolute;
    left: 20%;
    top: 20px;
    font-weight: bold;
    font-size: 20px;
    color: white;
    .valueText{
      font-weight: 200;
      font-size: 16px;
    }
  }
}
.imgItem:nth-child(2){
  padding-top:120px ;
}
</style>

引入的stereographicRepresentation 组件代码:

<template>
  <div class="contentss">
    <div class="ets1" ref="ets1"></div>
    <div class="ets2" ref="ets2"></div>
  </div>

</template>

<script>
import * as echarts from 'echarts'
import 'echarts-gl';
export default {
  name: "index",
  data() {
    return {
      pointObj:null,
      echartsItem:null
    };
  },
  props:{
    dataList:{
      type:Object,
      default:()=>{}
    }
  },
  watch:{
    dataList:{
      handler(val){
        if(val){
          let pointObj = {}
         val.threeRatioMethodResultVOList.forEach(item=>{
            return pointObj[item.ratioName]=item.ratioValue
          })
          this.pointObj = pointObj
          this.$emit('returnValue',pointObj)
          this.setScatter(pointObj)
        }
      },
      immediate:true
    }
  },
  computed:{
    //计算出对应的值
    getValue() {
      return (value)=>{
        if(value>0&&value<=0.1){
          return value/0.1*( 10/3 );
        }else if(value>0.1&&value<=1){
          return (value-0.1)/0.9*(10/3) + 10/3
        }else if(value>1&&value<=10){
          return (value-1)/9*(10/3) + 20/3
        }
      }
    }
  },
  mounted() {
    this.$nextTick(()=>{
      this.setOneCharts()
      this.setTwoCharts()
    })
  },
  methods:{
    setScatter(pointObj){
      let seriesItem = {
        type: 'scatter3D',
        data: [
          [pointObj['C2H4/C2H6'], pointObj['CH4/H2'], pointObj['C2H2/C2H4']]
        ],
        itemStyle: {
          color: '#ff0000'
        },
      }
      console.log('this.echartsItem',this.echartsItem)
      this.echartsItem.setOption({
        series:[seriesItem]
      })
    },
    getOption(){
      console.log(this.echarts2.getOption())
    },
    initE(option,refs,isShow = false){
      let echartsItem = echarts.init(refs)
      if(isShow){
        this.echartsItem = echartsItem
      }
      echartsItem.setOption(option)
    },
    setOneCharts(){
      let option = {
        tooltip: {},
        grid3D: {
          viewControl: {
            alpha: 14,
            beta: 118,
            distance: 240,
            rotateSensitivity: 0,
            // 禁止鼠标滚轮缩放
            roamController: {
              zoom: false
            }
          },
        },
        xAxis3D: {
          type: 'log', // 使用对数坐标轴
          name: 'C2H4/C2H6',
          nameGap: 10,
          axisLabel: {
            color: '#fff'
          },
          scale: true, // 开启坐标轴标尺
          min: 0.01, // 设置刻度最小值
          max: 10, // 设置刻度最大值
          axisTick: { // 设置刻度线
            show: true,
            lineStyle: {
              width: 3,
              color:'#fff'
            },
            // 设置刻度线位置
            data: [0.1, 1, 10]
          },
          axisLine: { // 设置坐标轴线
            lineStyle: {
              width: 3,
              color:'#fff'

            }
          }
        },
        yAxis3D: {
          type: 'log',
          name: 'CH4/H2',
          nameGap: 10,
          axisLabel: {
            color: '#fff'
          },
          scale: true,
          min: 0.01,
          max: 10,
          axisTick: {
            show: true,
            lineStyle: {
              width: 3,
              color:'#fff'
            },
            data: [0.1, 1, 10]
          },
          axisLine: {
            lineStyle: {
              width: 3,
              color:'#fff'

            }
          }
        },
        zAxis3D: {
          type: 'log',
          name: 'C2H2/C2H4',
          nameGap: 10,
          axisLabel: {
            color: '#fff',
          },

          scale: true,
          min: 0.01,
          max: 10,
          axisTick: {
            show: true,
            lineStyle: {
              width: 3,
              color:'#fff'
            },
            data: [0.1, 1, 10]
          },

          axisLine: {
            lineStyle: {
              width: 3,
              color:'#fff'
            }
          }
        },
        toolbox: {
          feature: {
            saveAsImage: {
              show: false // 禁用保存图片功能,同时禁止鼠标滚轮事件
            }
          }
        },
        series: []
      };
      this.initE(option,this.$refs.ets1)
    },
    setTwoCharts(){
      let option =  {
        dataZoom: [
          {
            type: 'inside', // 内置型数据区域缩放组件
            disabled: true // 禁用数据区域缩放
          }
        ],
        toolbox: {
          feature: {
            saveAsImage: {
              show: false // 禁用保存图片功能,同时禁止鼠标滚轮事件
            }
          }
        },
        xAxis3D: {
          type: 'value',
          min:0,
          max:10
        },
        yAxis3D: {
          type: 'value',
          min:0,
          max:10

        },
        zAxis3D: {
          type: 'value',
          min:0,
          max:10
        },
        grid3D: {
          show: false,
          viewControl: {
            alpha: 14,
            beta: 118,
            distance: 240,
            rotateSensitivity: 0,
            // 禁止鼠标滚轮缩放
            roamController: {
              zoom: false
            }
          },
          light: {
            main: {
              shadow: true,
              quality: 'ultra',
              intensity: 1
            }
          }
        },
        series: [{
          type: 'bar3D',
          name:'T1',
          data: [
            [this.getValue(0.1),this.getValue(2.5)+1.1,0,'T1'], // 数据点的位置
          ],
          barSize: [66,33.3333,0],
          itemStyle:{
            opacity:0.5
          },
          label: {
            show: true,  // 显示标签
            textStyle:{
              fontWeight :'bold',
              fontSize:'22px'
            },
            formatter: (obj)=>{
              return obj.seriesName;
            }
          }
        },
          {
            type: 'bar3D',
            name:'T2',
            data: [
              [this.getValue(2.5),this.getValue(1)+1.7,this.getValue(0.1)], // 数据点的位置
            ],
            barSize: [11,33,0],
            itemStyle:{
              opacity:0.5
            },
            label: {
              show: true,  // 显示标签
              textStyle:{
                fontWeight :'bold',
                fontSize:'22px'
              },
              formatter: (obj)=>{
                return obj.seriesName;
              }
            }
          },
          {
            type: 'bar3D',
            name:'T3',
            data: [
              [this.getValue(7),this.getValue(1)+1.7,this.getValue(0.2)], // 数据点的位置
            ],
            barSize: [22,33,0],
            itemStyle:{
              opacity:0.5
            },
            label: {
              show: true,  // 显示标签
              distance:0,
              textStyle:{
                fontWeight :'bold',
                fontSize:'22px'
              },
              formatter: (obj)=>{
                return obj.seriesName;
              }
            }
          },
          {
            type: 'bar3D',
            name:'PD',
            data: [
              [this.getValue(0.05)+0.366,this.getValue(0.05),0], // 数据点的位置
            ],
            barSize: [33.18,33,0],
            itemStyle:{
              opacity:0.5
            },
            label: {
              show: true,  // 显示标签
              distance:0,
              textStyle:{
                fontWeight :'bold',
                fontSize:'22px'
              },
              formatter: (obj)=>{
                return obj.seriesName;
              }
            }
          },
          //透明基座
          {
            // 透明的“基座”系列
            type: 'bar3D',
            stack: 'stack',
            data: [
              [this.getValue(5.5),this.getValue(0.278),this.getValue(1)], // 数据点的位置
            ],
            barSize: [33.33,14.67,0],

            itemStyle: {
              opacity: 0 // 透明度设置为0,使其不可见
            }
          },
          {
            type: 'bar3D',
            name:'D1',
            stack: 'stack',

            data: [
              [this.getValue(5.5),this.getValue(0.278),this.getValue(0.1)], // 数据点的位置
            ],
            barSize: [33.33,14.67,0],
            itemStyle:{
              opacity:0.5
            },
            label: {
              show: true,  // 显示标签
              distance:0,
              textStyle:{
                fontWeight :'bold',
                fontSize:'22px'
              },
              formatter: (obj)=>{
                return obj.seriesName;
              }
            },
            dataFilter: {
              // 过滤掉第一个柱子
              data: [
                [0, NaN, NaN],
                [1, 0, 80],
              ]
            }
          },

          //透明基座
          {
            // 透明的“基座”系列
            type: 'bar3D',
            stack: 'stack2',
            data: [
              [this.getValue(4)+0.7,this.getValue(0.45)+0.4,this.getValue(0.6)], // 数据点的位置
            ],
            barSize: [33.33,14.67,0],

            itemStyle: {
              opacity: 0 // 透明度设置为0,使其不可见
            }
          },
          {
            type: 'bar3D',
            name:'D2',
            stack: 'stack2',

            data: [
              [this.getValue(4)+0.7,this.getValue(0.45)+0.4,2.36], // 数据点的位置
            ],
            barSize: [29.33,33.37,0],
            itemStyle:{
              opacity:0.5
            },
            label: {
              show: true,  // 显示标签
              distance:0,
              textStyle:{
                fontWeight :'bold',
                fontSize:'22px'
              },
              formatter: (obj)=>{
                return obj.seriesName;
              }
            },
          }
        ]
      };
      this.initE(option,this.$refs.ets2,true)
    }
  }
}
</script>

<style scoped>
.contentss{
  width: 600px;
  display: inline-block;
  height: 600px;
  position: relative;
  .ets1{
    margin: 0 auto;
    width: 600px;
    height: 600px;
    /*background: #ffffff;*/
    position: absolute;
    left: 0;
    top: 0;
  }
  .ets2{
    margin: 0 auto;
    width: 600px;
    height: 600px;
    position: absolute;
    /*background: #ffffff;*/
    left: 0;
    top: 0;
  }
}

</style>

代码所需大卫三角形图片:

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值