气体比值的图示法:
实现:
需求:
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']}} C<sub>2</sub>H<sub>4</sub>/C<sub>2</sub>H<sub>6</sub>:{{echartsValueObj['C2H4/C2H6']}} 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)}}% %CH<sub>4</sub>: {{ CH4_percent.toFixed(2) }}% %C<sub>2</sub>H<sub>4</sub>: {{ C2H4_percent.toFixed(2) }}% </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>
代码所需大卫三角形图片: