Echarts散点图(火山图)自定义配置

devtools/2024/12/4 2:33:12/
  1. 创建DOM元素

    vue"><div><div ref="chat" :style="{width: volcanoDimensions.width,height: volcanoDimensions.height,}" class="chat"></div></div>
    
  2. 初始化图表

    在这里插入图片描述

    需要实现,被筛选出的数据,带有label,labelLayout,labelLine,原定是直接在series的三组数据中,直接给出三者的相关配置:

    label: {show: true,formatter: function (param) {// 仅为目标点显示标签const specialPoints = ["a", "b"];if (specialPoints.includes(param.value[2])) {return param.value[2];}return "";},position: 'left',minMargin: 2
    },
    labelLine:{show: true
    },
    labelLayout: function (labelInfo) {// 从后端获取的匹配字段,当前给定['a','b']const specialPoints = ["a", "b"];if (specialPoints.includes(labelInfo.text)) {return {x: myChart.getWidth() - 500,moveOverlap: 'shiftY'};}return ""; // 非特定点不应用布局
    },
    

    目的是为了匹配后端返回的过滤数据,给出对应label和labelLine,如果当前点符合,才显示对应标签;但是页面显示label的时候:

    在这里插入图片描述

    未匹配的点周围也会显示label,只是很短。尝试给labelLine加上条件判断,返回bool值。但是并没有起作用。

    label : {show: function (param) {// 仅为目标点显示标签线const specialPoints = ["a", "b"];return specialPoints.includes(param.value[2]);},
    }
    

    最后只能通过,筛选匹配数据,根据name往series里面push两组新数据作为额外两组数据,单独显示label

    如果是饼图,这种series里面只有一组数据,单独配置的话:

    直接通过遍历里面的数据,单独添加labellabelLine属性会更方便,两种方法均可行

    let label = {show:true,fontSize:14,color:'#000'
    }
    let labelLine = {show: true,lineStyle:{color: '#000'}
    }
    series.data.forEach((item,index)=>{const testArr = ['a','b','c']if(testArr.includes(item.value)){item['label'] = labelitem['labelLine'] = labelLine}
    })
    
    data() {return {myChart: this.$refs.chat,// 默认初始配置volcanoChartOptions:{}};
    },mounted() {// 先初始化,之后从接口访问这个series数据// 理论上,所有的图表数据都应该是后端返回this.initChat();// 从接口返回图表配置数据this.loadvolcanoChartOptions();
    },methods:{initChat() {// 只能写从后端获取数据的时候,往series后面重新push一个新的对象,// 然后这个对象的其余配置都和对应name保持一致,但是label的显示隐藏不一致if (!this.myChart) {this.myChart = this.echarts.init(this.$refs.chat);}},// 加载后端数据async loadvolcanoChartOptions(){try{// 假设从接口模拟const response = await this.fetchvolcanoChartOptions()// console.log(response.data,"----");this.volcanoChartOptions = response.data// 将数据更新到vuex中this.updateVolcanoOptions(response.data)this.volcanoChartOptions && this.myChart.setOption(this.volcanoChartOptions);}catch(e){console.log("加载图表配置失败",e);}},// 创建接口,模拟从后端返回数据async fetchvolcanoChartOptions() {// 在这里替换成实际的 API 调用return new Promise((resolve) => {setTimeout(() => {resolve({data: {title: {text:'火山图',left: 'center',textStyle:{fontFamily: 'Arial',fontSize: 18,color:'rgba(0,0,0, 1)'}},grid: {left: '3%',right: '7%',bottom: '7%',containLabel: true,},tooltip: {// trigger: 'axis',showDelay: 0,formatter: function (params) {if (params.value.length > 1) {return ('X: ' + params.value[0] +'<br/>' +'Y: ' + params.value[1] +'<br/>' +'geneId: ' + params.value[2]);}},axisPointer: {show: true,type: 'cross',lineStyle: {type: 'dashed',width: 1}}},legend: {data: ['Up', 'Down', 'Nodiff'],right: '10%',top: '20%',orient: 'vertical',// 给图例的原点点加了个边框// itemStyle:{//   borderWidth:1,//   borderColor:'black'// }},xAxis: {type: 'value',scale: true,axisLabel: {formatter: '{value}',fontSize: 12,fontFamily: 'Arial',color: 'rgba(0,0,0, 1)',},splitLine: {show: false},min:'-25',max:'10',nameLocation:'middle',name:'x轴标题',nameGap: 30,  // 设置标题与轴的间隔nameTextStyle: { fontFamily: 'Arial', fontSize: 16,color: 'rgba(0,0,0, 1)',},},yAxis: {type: 'value',scale: false,axisLabel: {formatter: '{value}',fontSize: 12,fontFamily: 'Arial',color: 'rgba(0,0,0, 1)',},// 是否展示横线,每个刻度的横线splitLine: {show: false},min:'-5',max:'35',nameLocation:'middle',name:'y轴标题',nameGap: 30,  // 设置标题与轴的间隔nameTextStyle: { fontFamily: 'Arial', fontSize: 16,color: 'rgba(0,0,0, 1)',},},series:[{name: 'Nodiff',color: 'rgba(192,192,192, 1)',type: 'scatter',symbolSize:5, // 标记点的大小emphasis: {focus: 'series'},data: [[-0.8,8.2,"ENSMUSG00000030342"],[-0.7,8.1,"ENSMUSG00000004633"],[-0.9,7.6,"ENSMUSG00000074802"],],markLine: {lineStyle: {type: 'solid'},}},{name: 'Up',color: 'rgba(255, 51, 51,1)',type: 'scatter',symbolSize:5, // 标记点的大小emphasis: {focus: 'series'},// prettier-ignoredata: [[1.262,4.1255,"a"],[3.8047,3.5476,"b"],[1.2933,3.1948,"c"],[1.6687,2.7994,"ENSMUSG00000084844"],[7.1892,2.7479,"ENSMUSG00000000394"],[1.0471,2.6834,"ENSMUSG00000084904"],[1.835,2.5167,"ENSMUSG00000028707"],[1.1921,2.333,"ENSMUSG00000086502"],[1.1156,2.3094,"ENSMUSG00000028125"],[4.3545,2.2873,"ENSMUSG00000107313"],],markLine: {silent: true,symbol: 'none', // 去掉箭头data: [// label label: {color: '#FF1D00',formatter:'',fontSize:10} formatter里面写echart横线竖线的标注{ xAxis: 'min', lineStyle: { color: '#2f2f2f' }, label: { color: '#FF1D00', formatter: '', fontSize: 10 } },{ yAxis: 'min', lineStyle: { color: '#2f2f2f' }, label: { color: '#FF7804', formatter: '', fontSize: 10 } }]}},{name: 'Down',color: 'rgba(51, 51, 255, 1)',type: 'scatter',symbolSize:5, // 标记点的大小emphasis: {focus: 'series'},// prettier-ignoredata: [[-3.445,29.9859,"a"],[-1.418,25.7115,"b"],[-1.489,25.1274,"c"],[-3.445,29.9859,"a"],[-1.418,25.7115,"b"],[-1.489,25.1274,"c"],[-3.28,23.0754,"ENSMUSG00000056071"],[-1.999,19.1505,"ENSMUSG00000001741"],[-3.1,18.2834,"ENSMUSG00000056054"],[-1.649,16.959,"ENSMUSG00000025154"],[-2.92,16.3029,"ENSMUSG00000020264"],[-2.652,11.7498,"ENSMUSG00000037868"],[-4.807,10.8801,"ENSMUSG00000040380"],],markLine: {silent: true,symbol: 'none', // 去掉箭头data: [// label label: {color: '#FF1D00',formatter:'',fontSize:10} formatter里面写echart横线竖线的标注{ xAxis: 'max', lineStyle: { color: '#2f2f2f' }, label: { color: '#FF1D00', formatter: '', fontSize: 10 } }]}},// push的数组,加上光圈的两组数据,即为筛选后的数据{name: 'Down',color: 'rgba(51, 51, 255, 1)',type: 'effectScatter',  //加上光圈效果,如果做切换,scatter就是纯点symbolSize:6,  //光圈大小emphasis: {focus: 'series'},label: {show: true,formatter: function (param) {return param.value[2]; // 显示 geneId},position: 'left',minMargin: 2,fontSize: 12,fontFamily: 'Arial',fontStyle: 'normal',  // 正常样式normal;斜体样式italiccolor:'rgba(6, 6, 6, 1)', //标签字体颜色},// labelline不受return的控制,不受funtion的控制labelLine: {show: true,lineStyle: {color: 'rgba(48, 49, 51, 1)',type: 'solid', //线条形状}},labelLayout: {x:this.myChart.getWidth() - 400,y:this.myChart.getHeight()/3,moveOverlap: 'shiftY'},// prettier-ignoredata: [[-3.445,29.9859,"a"],[-1.418,25.7115,"b"],[-1.489,25.1274,"c"]],},{name: 'Up',color: 'rgba(255, 51, 51,1)',type: 'effectScatter',symbolSize:6,  //光圈大小emphasis: {focus: 'series'},label: {show: true,formatter: function (param) {return param.value[2]; // 显示 geneId},position: 'right',minMargin: 2,fontSize: 12,fontFamily: 'Arial',fontStyle: 'normal',  // 正常样式normal;斜体样式italiccolor:'rgba(6, 6, 6, 1)', //标签字体颜色},// labelline不受return的控制,不受funtion的控制labelLine: {show: true,lineStyle: {color: 'rgba(48, 49, 51, 1)',type: 'solid',// type: [5, 10], // 长短虚线间隔 [长度, 间隔]}},labelLayout: {x: this.myChart.getWidth() - 100,// y: this.myChart.getHeight()/1.7 - (Math.random() * 40 - 60),moveOverlap: 'shiftY'},// prettier-ignoredata: [[1.262,4.1255,"a"],[3.8047,3.5476,"b"],[1.2933,3.1948,"c"]],}],},});}, 1000); // 模拟网络延迟});},
    }
    
  3. 数据监听

    为了动态配置修改echarts的数据,采取VueX来实现数据的更新(频繁更新数据不调取接口,只有初始渲染和保存参数时才调用接口)

    watch: {// 改变颜色,监听的整个optionsvolcanoOptionsState: {deep:true,handler(newOptions) {// console.log(newOptions,"newOptions");if (this.myChart) {// 动态刷新this.myChart.setOption(newOptions,true);}}},// 宽高volcanoDimensions:{deep:true,handler(newDimensions) {if (this.myChart) {const newWidth = this.convertRemToPx(newDimensions.width);const newHeight = this.convertRemToPx(newDimensions.height);this.myChart.resize({ width: newWidth, height: newHeight });}}},
    },
    computed: {...mapState('echarts', ['volcanoOptionsState','volcanoDimensions','volcanoStyle']),
    },
    methods:{// 转换 rem 为 px,宽高前期没用百分比,所以涉及到单位转换convertRemToPx(rem) {const remToPx = parseFloat(getComputedStyle(document.documentElement).fontSize); // 获取 1rem 等于多少pxif (rem.endsWith('rem')) {return parseFloat(rem) * remToPx;}return rem; // 如果传入的不是 rem,直接返回原值},
    }
    
  4. 数据修改

    以其中一个配置项为例

    vue"><div class="itemList"><div class="item"><span>宽</span><el-input-number v-model="volcanoWidth" controls-position="right" :min="1"></el-input-number></div><div class="item"><span>高</span><el-input-number v-model="volcanoHeight" controls-position="right" :min="1"></el-input-number></div><div class="item"><span>Down</span><el-color-picker v-model="volcanoColorsDown" show-alpha></el-color-picker></div><div class="item"><span>Up</span><el-color-picker v-model="volcanoColorsUp" show-alpha></el-color-picker></div><div class="item"><span>Nodiff</span><el-color-picker v-model="volcanoColorsNodiff" show-alpha></el-color-picker></div><div class="item"><span>图表样式</span><el-checkbox-group @change="changeVolcanoStyle" v-model="volcanoCheckbox"><el-checkbox label="显示边框"></el-checkbox><el-checkbox label="散点光圈"></el-checkbox></el-checkbox-group></div><div class="item"><span>散点大小</span><el-input-number v-model="volcanoScatterSizeNum" controls-position="right"></el-input-number></div><div class="item"><span>x/y轴偏移</span><div class="buttonGroup"><el-button @click="moveAxis('yAxis', 'decrease', 'min')">↑</el-button><div><el-button @click="moveAxis('xAxis', 'increase', 'max')">←</el-button><el-button @click="moveAxis('xAxis', 'decrease', 'min')">→</el-button></div><el-button @click="moveAxis('yAxis', 'increase', 'max')">↓</el-button></div></div>
    </div>
    volcanoWidth:{get(){return parseFloat(this.volcanoDimensions.width)},set(newWidth){this.updateVolcanoWidth(newWidth);}
    },
    volcanoHeight:{get(){return parseFloat(this.volcanoDimensions.height)},set(newHeight){this.updateVolcanoHeight(newHeight);}
    },
    volcanoColorsDown:{get(){return this.volcanoColors.down},set(newColor){this.updateVolcanoColorDown({name: 'Down', newColor});}
    },
    volcanoColorsUp:{get(){return this.volcanoColors.up},set(newColor){this.updateVolcanoColorUp({name: 'Up', newColor});}
    },
    

    就是基础的通过Vuex去监听数据的变化,通过set响应式修改,更新options刷新echarts图表。

  5. 代码封装

    动态生成getter和setter,以此类推

    // 宽高配置
    ...['width', 'height'].reduce((acc, dimension) => {acc[`volcano${dimension.charAt(0).toUpperCase() + dimension.slice(1)}`] = {get() {return parseFloat(this.volcanoDimensions[dimension]);},set(newValue) {const currentValue = parseFloat(this.volcanoDimensions[dimension]);if (currentValue !== newValue) {this[`updateVolcano${dimension.charAt(0).toUpperCase() + dimension.slice(1)}`](newValue);}}};return acc;
    }, {}),// 对于 x 轴和 y 轴的处理
    ...['X', 'Y'].reduce((acc, axis) => {['Title', 'TitleFontSize', 'TitleColor', 'LabelFontSize', 'LabelColor'].forEach(property => {const propName = `volcano${axis}${property}Value`;acc[propName] = {get() {return this[`volcano${axis}Axis${property}`];},set(newValue) {this[`updateVolcano${axis}${property}`](newValue); // 更新相应的 x/y 轴属性}};});return acc;
    }, {}),
    
  6. Echarts相关配置项对应

    • 标题
    title: {text:'火山图',    //名称left: 'center',     //居中/居左/居右textStyle:{fontFamily: 'Arial',fontSize: 18,color:'rgba(0,0,0, 1)'}
    },
    
    • 图表整体偏移
    grid: {left: '3%',    //相对偏移百分比right: '7%',bottom: '7%',containLabel: true,},
    
    • 鼠标移入显示信息
    tooltip: {// 鼠标移入显示参数,parmas为当前点的信息['x','y','自定义数据']formatter: function (params) {if (params.value.length > 1) {return ('X: ' + params.value[0] +'<br/>' +'Y: ' + params.value[1] +'<br/>' +'geneId: ' + params.value[2]);}},},
    
    • 图例
    legend: {data: ['Up', 'Down', 'Nodiff'],right: '10%',top: '20%',orient: 'vertical',// 给图例的原点点加了个边框// itemStyle:{//   borderWidth:1,//   borderColor:'black'// }},
    
    • x/y轴配置
    xAxis: 
    {type: 'value',scale: true,axisLabel: {formatter: '{value}',fontSize: 12,fontFamily: 'Arial',color: 'rgba(0,0,0, 1)',},min:'-25',max:'10',nameLocation:'middle',  //位置name:'x轴标题',nameGap: 30,  // 设置标题与轴的间隔nameTextStyle: { fontFamily: 'Arial', fontSize: 16,color: 'rgba(0,0,0, 1)',},
    },
    
    • series(以里面的一组数据为例)
    {name: 'Nodiff',color: 'rgba(192,192,192, 1)',type: 'scatter',   //否显示散点光圈,为effectScatter就是有,scatter就是纯点symbolSize:6,   //散点光圈大小,type为effectScatter时生效symbolSize:5, // 标记点的大小data:[[1,2,'自定义数据',{'a','b'}]],label: {show: true,formatter: function (param) {return param.value[2]; // 显示 '自定义数据'},position: 'left',minMargin: 2,fontSize: 12,fontFamily: 'Arial',fontStyle: 'normal',  // 正常样式normal;斜体样式italiccolor:'rgba(6, 6, 6, 1)', //标签字体颜色},labelLine: {show: true,lineStyle: {color: 'rgba(48, 49, 51, 1)',type: 'solid', //线条形状dashed/solid/dotted}},labelLayout: {x:this.myChart.getWidth() - 400,y:this.myChart.getHeight()/3,moveOverlap: 'shiftY'},
    }
    

http://www.ppmy.cn/devtools/139230.html

相关文章

Vue3 脚手架扩展

当 yarn dev 运行成功后&#xff0c;我们继续添加扩展 首先我们要安装一些依赖 其中的vue-router和vuex安装最新版的就行&#xff0c;因为项目是vue3 element-plus和less&#xff0c;less-loader最好按照我这个版本来下载 element-plus是一个vue常用的ui组件库 element-plus/…

Flutter 1.1:下载Flutter环境

1、在AS中下载Flutter插件 在setting的Plugins中下载Flutter&#xff0c;如图所示&#xff0c;可以直接进行搜索查找 2、下载flutter的sdk源代码 flutter中文文档学习 通过Git下载SDK源代码 git clone -b stable https://github.com/flutter/flutter.git3、配置系统变量 3…

使用pymupdf提取PDF文档中的文字和其颜色

最近我在捣鼓一个PDF文件&#xff0c;想把它里面的文字和文字颜色给提取出来。后来发现有个叫pymupdf的库能搞定这事儿。操作起来挺简单的&#xff0c;pymupdf的示例文档里就有现成的代码可以参考。 how-to-extract-text-with-color 我本地的测试代码如下&#xff1a; impor…

SQL进阶技巧:非等值连接--单向近距离匹配

目录 0 场景描述 1 数据准备 2 问题分析 ​编辑 ​编辑 3 小结 数字化建设通关指南 0 场景描述 表 t_1 和表 t_2 通过 a 和 b 关联时,有相等的取相等的值匹配,不相等时每一 个 a 的值在 b 中找差值最小的来匹。 表 t_1:a 中无重复值 表 t_1:a 中无重复值 a 1 2 4 …

3D Gaussian Splatting综述 论文笔记

文章目录 参考资料 参考资料 A Survey on 3D Gaussian Splatting精读 3DGaussianSplatting技术的影响会有多大&#xff1f; - Kedreamix的回答 - 知乎 https://www.zhihu.com/question/626506306/answer/3375816824 3DGS综述《A Survey on 3D Gaussian Splatting》 【论文笔记…

Docker 容器隔离关键技术:SELinux

Docker 容器隔离关键技术&#xff1a;SELinux SELinux&#xff08;Security-Enhanced Linux&#xff09; 是 Linux 内核中的一项安全机制&#xff0c;用于实现强制访问控制&#xff08;MAC&#xff09;。Docker 利用了 SELinux 来增强容器的隔离性&#xff0c;通过对文件、进程…

AppFlow:支持飞书机器人调用百炼应用

AppFlow&#xff1a;支持飞书机器人调用百炼应用 简介&#xff1a; 本文介绍了如何创建并配置飞书应用及机器人&#xff0c;包括登录飞书开发者后台创建应用、添加应用能力和API权限&#xff0c;以及通过AppFlow连接流集成阿里云百炼服务&#xff0c;最后详细说明了如何将机器…

PHP和GD如何给图片添加模糊边缘效果

在PHP中&#xff0c;使用GD库给图片添加模糊边缘效果通常涉及几个步骤&#xff0c;包括加载图片、创建模糊效果、应用边缘处理以及保存结果图片。然而&#xff0c;GD库本身并不直接提供边缘模糊的函数&#xff0c;但你可以通过一些技巧来实现类似的效果。 以下是一个简化的步骤…