-
创建DOM元素
vue<div> <div ref="chat" :style="{ width: volcanoDimensions.width, height: volcanoDimensions.height, }" class="chat"></div> </div>
-
初始化图表
需要实现,被筛选出的数据,带有
label,labelLayout,labelLine
,原定是直接在series
的三组数据中,直接给出三者的相关配置:jslabel: { 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值。但是并没有起作用。jslabel : { show: function (param) { // 仅为目标点显示标签线 const specialPoints = ["a", "b"]; return specialPoints.includes(param.value[2]); }, }
最后只能通过,筛选匹配数据,根据
name
往series里面push
两组新数据作为额外两组数据,单独显示label
。如果是饼图,这种
series
里面只有一组数据,单独配置的话:直接通过遍历里面的数据,单独添加
label
和labelLine
属性会更方便,两种方法均可行jslet 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'] = label item['labelLine'] = labelLine } })
jsdata() { 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-ignore data: [ [ 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-ignore data: [ [ -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;斜体样式italic color:'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-ignore data: [[ -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;斜体样式italic color:'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-ignore data: [[ 1.262, 4.1255, "a" ], [ 3.8047, 3.5476, "b" ], [ 1.2933, 3.1948, "c" ]], } ], }, }); }, 1000); // 模拟网络延迟 }); }, }
-
数据监听
为了动态配置修改echarts的数据,采取
VueX
来实现数据的更新(频繁更新数据不调取接口,只有初始渲染和保存参数时才调用接口)jswatch: { // 改变颜色,监听的整个options volcanoOptionsState: { 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 等于多少px if (rem.endsWith('rem')) { return parseFloat(rem) * remToPx; } return rem; // 如果传入的不是 rem,直接返回原值 }, }
-
数据修改
以其中一个配置项为例
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>
jsvolcanoWidth:{ 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图表。 -
代码封装
动态生成getter和setter,以此类推
js// 宽高配置 ...['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; }, {}),
-
Echarts相关配置项对应
- 标题
jstitle: { text:'火山图', //名称 left: 'center', //居中/居左/居右 textStyle:{ fontFamily: 'Arial', fontSize: 18, color:'rgba(0,0,0, 1)' } },
- 图表整体偏移
jsgrid: { left: '3%', //相对偏移百分比 right: '7%', bottom: '7%', containLabel: true, },
- 鼠标移入显示信息
jstooltip: { // 鼠标移入显示参数,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] ); } }, },
- 图例
jslegend: { data: ['Up', 'Down', 'Nodiff'], right: '10%', top: '20%', orient: 'vertical', // 给图例的原点点加了个边框 // itemStyle:{ // borderWidth:1, // borderColor:'black' // } },
- x/y轴配置
jsxAxis: { 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(以里面的一组数据为例)
js{ 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;斜体样式italic color:'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' }, }
Echarts散点图(火山图)自定义配置
伊丶二2024-12-01 17:00
相关推荐
低调函数29 分钟前
TypeScript和JavaScript的区别中东大鹅1 小时前
【JavaScript】下拉框的实现Domain-zhuo1 小时前
什么是前端构建工具?比如(Vue2的webpack,Vue3的Vite)yanmengying1 小时前
VUE脚手架练习突然暴富的我1 小时前
html button 按钮单选且 高亮午后书香2 小时前
看两道关于异步的字节面试题...心肝到爆2 小时前
vue3项目最新eslint9+prettier+husky+stylelint+vscode配置爱吃羊的老虎3 小时前
【WEB开发.js】addEventListener事件监听器的绑定和执行次数的问题(小心踩坑)工业互联网专业4 小时前
Python毕业设计选题:基于Flask的医疗预约与诊断系统LogicFlow4 小时前
👋一起来给流程图加便签吧,超级简单!