echarts柱状图可鼠标左击出现自定义弹框,右击隐藏弹框并阻止默认右击事件

每项x轴数据对应有两条柱图和一条阴影效果是学习其它博客得到的效果,这个是学习的原文链接:echarts两个合并柱体(普通柱状图+象形柱图)共享一个柱体阴影

因为这次情况比较特殊,不仅需要自定义弹框内容,而且也需要鼠标的右击事件隐藏效果。这里我就假设在已有阴影效果的基础上,针对鼠标点击显示弹框进行记录。若有需要源码在后面,可自取。

echarts自定义弹框内容

效果图展示

前记

echarts的鼠标事件:点击跳转至官方的鼠标事件

官网给的事件行为逻辑:点击跳转至官网的事件与行为

下面是具体含义:

click:当用户在图表上进行单击操作时触发,可以用于实现单击选中数据点或者执行特定的交互操作。

dblclick:当用户在图表上进行双击操作时触发,可以用于实现双击放大或者展开详细信息等功能。

mousedown:当用户按下鼠标按钮时触发,可以用于实现拖拽、绘制选框等交互操作。

mousemove:当用户在图表上移动鼠标时触发,可以用于实现实时显示数据信息或者跟随鼠标移动的提示框。

mouseup:当用户释放鼠标按钮时触发,可以用于结束拖拽、绘制等操作。

mouseover:当鼠标悬停在图表元素上时触发,可以用于显示详细信息或者高亮当前元素。

mouseout:当鼠标移出图表元素时触发,可以用于隐藏信息框或者取消高亮状态。

globalout:当鼠标移出图表区域时触发,可以用于清除所有高亮状态或者隐藏全局提示框。

contextmenu:当用户在图表上右键单击时触发,可以用于显示自定义的右键菜单或者执行特定的操作。

这里用到的是click单击以及contextmenu右键单击事件。

鼠标左击事件(click)

这里在绑定点击操作里,根据被点击的位置使用子绝父相(子盒子position: absolute;父盒子position: relative;),设定自定义弹框的位置。

参数介绍

点击阴影部分时,可查看params值,params.event.event与vue的鼠标右击事件 @contextmenu="func"传的event参数一样,通过这个可以设置被点击盒子的显示位置,设定其top值及left值。

自定义弹框的样式

1、若添加自定义类名可通过divdom.classList.add("类名") ,在 JavaScript 中,classList 是 DOM 元素的属性,它提供了一组方法来操作元素的类名。其中,.add() 方法用于向元素的类列表中添加一个或多个类名。

2、若添加自定义样式可通过divdom.style.cssText,此方法是用于设置元素的内联样式的属性。直接将 CSS 样式规则作为字符串赋给元素的内联样式,从而一次性设置多个样式属性。

单引号与反引号的区别:

反引号是 ES6 中的模板字符串语法。使用反引号包裹字符串可以实现字符串插值,即在字符串中嵌入变量或表达式的值。

与单引号和双引号相比,使用反引号的优势在于可以在字符串中直接引用变量,而无需使用字符串连接符(+)来拼接字符串。这样使得代码更具可读性,并且更容易维护。

在下面这段代码中,反引号用于创建一个模板字符串,其中 ${} 内部的表达式会被计算并替换为实际的值。这样就可以动态地设置 left 和 top 样式属性的值,根据 params.event.event.pageX 和 params.event.event.pageY 的实时数值来确定自定义div菜单的位置。

逻辑代码部分

添加自定义元素

通过 createElement 方法创建视图元素,这里创建了三个div,分别为父盒子(.custom-menu)、两个子盒子(.menu-item),元素结构如下图。

清除之前的菜单元素

自定义弹框已经创建显示好了,每次左击时都会出现一个新创建的弹框,接下来就需要隐藏之前出现的,如何拿到之前的弹框呢?那么就需要一个全局参数 currentMenu: null 记录当前菜单,用于判断是否存在菜单并使用 removeChild 方法删除子节点。

这里分为两种情况使用清除,一是每次左击时清除之前被左击显示的菜单;二是点击菜单里的某项子盒子时清除盒子。清除逻辑都差不多,只不过需要分情况调用而已。

左击方法的全部代码

javascript 复制代码
chartLeftEvent(chart) {
      chart.on("click", (params) => {
        console.log("params", params);
        if (this.currentMenu && this.currentMenu.parentNode === document.body) {
          document.body.removeChild(this.currentMenu); // 清除之前的菜单元素
        }

        const menu = document.createElement("div");// 创建一个新的 div 元素,并将其赋给名为 menu 的常量
        menu.classList.add("custom-menu");// 为新创建的 div 元素添加一个自定义样式类名 "custom-menu"
        // 使用模板字符串设置 div 元素的内联样式,动态确定菜单的位置
        menu.style.cssText = `
    left: ${params.event.event.pageX}px;
    top: ${params.event.event.pageY}px;
  `;
  		// 将创建好的 div 元素添加到页面的 body 元素中,显示出菜单
        document.body.appendChild(menu);

        const menuItem1 = document.createElement("div");
        menuItem1.className = "menu-item";
        menuItem1.style.cssText = `
          border-radius: 0px 10px 0px 0px;
        `;
        menuItem1.innerText = "查看具体信息1";
        menuItem1.onclick = () => {
          console.log("查看工步信息");
          if (this.currentMenu && this.currentMenu.parentNode === document.body) {
            document.body.removeChild(this.currentMenu); // 清除之前的菜单元素
          }
        };
        menu.appendChild(menuItem1);

        const menuItem2 = document.createElement("div");
        menuItem2.className = "menu-item";
        menuItem2.style.cssText = `
          border-radius: 0px 0px 0px 10px;
        `;
        menuItem2.innerText = "查看具体信息2";
        menuItem2.onclick = () => {
          console.log("查看人员信息");
          if (this.currentMenu && this.currentMenu.parentNode === document.body) {
            document.body.removeChild(this.currentMenu); // 清除之前的菜单元素
          }
        };
        menu.appendChild(menuItem2);

        this.currentMenu = menu; // 保存当前菜单元素
      });
    },

鼠标右击事件(contextmenu)

逻辑代码部分

chart.getZr().on 与 chart.on

在 echarts 中,chart.getZr().on 与 chart.on 的区别主要在于事件绑定的对象和事件触发的时机:

chart.on与chart.getZr().on的区别:
chart.on: 这种方式是在 echarts 实例上直接绑定事件监听器。通过 chart.on 可以监听 echarts实例上的特定事件,如鼠标点击图表、图例切换等。这种方式适用于监听 echarts 提供的交互事件,如点击图表某个系列时触发的事件。
chart.getZr().on: 这种方式是直接在 echarts 实例的底层渲染容器(即 ZRender 实例)上绑定事件监听器。通过chart.getZr() 可以获取到 ZRender 实例,然后使用 .on 方法可以在 ZRender 实例上监听各种原生 DOM事件,如鼠标点击、移动等。这种方式可以实现更加底层的事件监听,对 echarts 渲染的交互细节进行定制。

总体来说,使用 chart.getZr().on 可以监听更加底层的原生 DOM 事件,而 chart.on 则用于监听 echarts 封装的图表交互事件。这里的左击事件用的chart.on,右击事件用的chart.getZr().on ,大家都可以感受一下。

隐藏鼠标右击后的默认行为

右击图表示默认会显示下图这个菜单,所以需要通过preventDefault 方法阻止默认右击事件

右击方法的全部代码

javascript 复制代码
chartRightEvent(chart) {
      // 使用箭头函数
      chart.getZr().on("contextmenu", (params) => {
        params.event.preventDefault(); // 取消默认右击事件
        if (this.currentMenu && this.currentMenu.parentNode === document.body) {
          document.body.removeChild(this.currentMenu); // 清除之前的菜单元素
        }
      });
    },

源码

html 复制代码
<div style="width: 100%; height: 100%" id="barChart" ref="echartsContainer"></div>
javascript 复制代码
....
data() {
    return { 
    	currentMenu: null, // 保存当前显示的菜单元素
   };
},
mounted() {
    this.updateChartData();
  },
 methods: {
 // 柱状图
    updateChartData() {
      var xdata = [
        "ABCD01-01",
        "ABCD01-02",
        "ABCD01-03",
        "ABCD01-04",
        "ABCD01-05",
        "ABCD01-06",
        "ABCD01-07",
        "ABCD01-08",
        "ABCD01-09",
        "ABCD01-10",
        "ABCD01-11",
        "ABCD01-12",
        "ABCD01-13",
        "ABCD01-14",
        "ABCD01-15",
        "ABCD01-16",
        "ABCD01-17",
        "ABCD01-18",
        "ABCD01-19",
        "ABCD01-20",
        "ABCD01-21",
        "ABCD01-22",
        "ABCD01-23",
        "ABCD01-24",
      ];
      var ctdata = [
        21,
        18,
        34,
        31,
        22,
        18,
        18,
        18,
        18,
        18,
        18,
        18,
        21,
        15,
        34,
        31,
        22,
        18,
        18,
        18,
        18,
        18,
        18,
        18,
      ];
      var stdata = [
        20.5,
        15,
        31,
        31,
        18,
        9,
        9,
        9,
        9,
        9,
        9,
        9,
        20.5,
        15,
        31,
        31,
        18,
        9,
        9,
        9,
        9,
        9,
        9,
        9,
      ];
      var bgdata = [
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
        10,
      ];
      var maxValue = 34; //最大秒数
      var maxCenterName = "ABCD01-03"; //最大秒数对应的工站
      var threshold = 8; //差值
      const chartDom = document.getElementById("barChart");
      const chart = echarts.init(chartDom, "dark");
      var option;
      option = {
        backgroundColor: "transparent",
        // 预设颜色优先级低于series里定义的颜色,所以象形柱图不会被污染
        color: [
          {
            type: "linear",
            x: 0,
            y: 1,
            x2: 0,
            y2: 0,
            colorStops: [
              {
                offset: 0,
                color: "rgba(204, 218, 234,0)", // 0% 处的颜色
              },
              {
                offset: 1,
                color: "rgba(204, 218, 234,1)", // 100% 处的颜色
              },
            ],
          },
          {
            type: "linear",
            x: 0,
            y: 1,
            x2: 0,
            y2: 0,
            colorStops: [
              {
                offset: 0,
                color: "rgba(93, 207, 207,0)", // 0% 处的颜色
              },
              {
                offset: 1,
                color: "rgba(93, 207, 207,1)", // 100% 处的颜色
              },
            ],
          },
        ],
        grid: {
          left: "3%",
          right: "6%",
          bottom: "15%",
        },
        legend: {
          orient: "vertical", // 设置图例垂直排列
          right: 10, // 调整图例距离右侧的距离
          top: "top", // 设置图例位于上方
          itemWidth: 10, // 设置宽度
          itemHeight: 10, // 设置高度
          textStyle: {
            color: "#fff",
          },
        },
        xAxis: [
          {
            type: "category",
            axisTick: {
              show: false,
            },
            data: xdata,
            axisLabel: {
              show: true,
              color: "#ffffff",
            },
            axisLabel: {
              show: true,
              color: "#ffffff",
              fontSize: 12, // 设置字体大小为12
            },
          },
          {
            type: "category",
            show: false,
            data: this.xAxisData_,
          },
        ],
        yAxis: [
          {
            type: "value",
            name: "second",
            min: 0,
            max: 40,
            splitNumber: 5,
            nameTextStyle: {
              //y轴上方单位的颜色
              color: "#fff",
            },
            splitLine: {
              lineStyle: {
                color: "rgba(133, 139, 145,0.3)",
              },
            },
            axisLabel: {
              show: true,
              color: "#ffffff",
            },
          },
          {
            type: "value",
            alignTicks: true,
            show: false,
          },
          {
            type: "value",
            show: false,
          },
        ],
        dataZoom: [
          {
            type: "slider", // 设置为滑动条
            show: true, // 显示滑动条
            start: 0, // 滑动条起始位置
            end: 100 / (12 - 10) - 1, // 滑动条结束位置
            bottom: 10, // 滑动条距离底部的距离
            textStyle: {
              color: "#fff",
            },
            backgroundColor: "#acb7c3", // 设置背景颜色
            handleStyle: {
              color: "#2a82e4", // 设置左右两边的可滑动收缩竖线颜色
            },
          },
        ],
        series: [
          {
            name: "DATA1",
            data: ctdata,
            type: "bar",
            barWidth: 10,
            barGap: 1, //柱子之间间距
            yAxisIndex: 0,
            z: 2,
            label: {
              show: true,
              position: "top",
              color: "white",
            },
            // 标记线配置
            markLine: {
              data: [
                {
                  yAxis: maxValue, // 最大值的纵坐标
                  label: {
                    show: true,
                    position: "end",
                    formatter: "瓶颈CT:" + maxValue + "s\n" + maxCenterName, // 标记线的标签
                    textStyle: {
                      color: "red",
                    },
                  },
                  lineStyle: {
                    type: "dashed", // 设置为虚线
                    color: "red", // 标记线的颜色
                  },
                  symbol: ["none", "arrow"], // 取消箭头样式
                  silent: true, // 取消鼠标悬浮时的高亮效果
                },
              ],
            },
          },
          {
            name: "DATA2",
            data: stdata,
            type: "bar",
            barWidth: 10,
            yAxisIndex: 1,
            z: 2,
            label: {
              show: true,
              position: "top",
              color: "#66e1df",
            },
          },
          {
            data: bgdata,
            type: "bar",
            barWidth: 55,
            yAxisIndex: 2,
            xAxisIndex: 1,
            z: 1,
            itemStyle: {
              normal: {
                color: function (params) {
                  var ctValue = ctdata[params.dataIndex];
                  var stValue = stdata[params.dataIndex];
                  if (Math.abs(ctValue - stValue) > threshold) {
                    return "rgba(102, 77, 107,0.6)";
                  } else {
                    return "rgba(0,0,0,0.16)";
                  }
                },
              },
            },
          },
        ],
      };
      option && chart.setOption(option);
      this.chartRightEvent(chart);
      this.chartLeftEvent(chart);
    },
    chartRightEvent(chart) {
      chart.getZr().on("contextmenu", (params) => {
        params.event.preventDefault(); // 取消默认右击事件
        // 使用箭头函数
        if (this.currentMenu && this.currentMenu.parentNode === document.body) {
          document.body.removeChild(this.currentMenu); // 清除之前的菜单元素
        }
      });
    },
    chartLeftEvent(chart) {
      chart.on("click", (params) => {
        console.log("params", params);
        // let xIndex = chart.convertFromPixel({ seriesIndex: 0 }, [
        //   params.offsetX,
        //   params.offsetY,
        // ])[0]; // 阴影索引值
        // console.log("xIndex", xIndex);
        if (this.currentMenu && this.currentMenu.parentNode === document.body) {
          document.body.removeChild(this.currentMenu); // 清除之前的菜单元素
        }

        const menu = document.createElement("div");
        menu.classList.add("custom-menu"); // 添加自定义样式类名
        menu.style.cssText = `
    left: ${params.event.event.pageX}px;
    top: ${params.event.event.pageY}px;
  `;
        document.body.appendChild(menu);

        const menuItem1 = document.createElement("div");
        menuItem1.className = "menu-item";
        menuItem1.style.cssText = `
          border-radius: 0px 10px 0px 0px;
        `;
        menuItem1.innerText = "查看具体信息1";
        menuItem1.onclick = () => {
          console.log("查看工步信息");
          if (this.currentMenu && this.currentMenu.parentNode === document.body) {
            document.body.removeChild(this.currentMenu); // 清除之前的菜单元素
          }
        };
        menu.appendChild(menuItem1);

        const menuItem2 = document.createElement("div");
        menuItem2.className = "menu-item";
        menuItem2.style.cssText = `
          border-radius: 0px 0px 0px 10px;
        `;
        menuItem2.innerText = "查看具体信息2";
        menuItem2.onclick = () => {
          console.log("查看人员信息");
          if (this.currentMenu && this.currentMenu.parentNode === document.body) {
            document.body.removeChild(this.currentMenu); // 清除之前的菜单元素
          }
        };
        menu.appendChild(menuItem2);

        this.currentMenu = menu; // 保存当前菜单元素
      });
    },
    }
css 复制代码
/* echarts图表样式 */
.custom-menu {
  position: absolute;
  width: 130px;
  box-shadow: rgb(0 0 0 / 30%) 1px 1px 3px;
  z-index: 999;
  background: rgb(69 140 199 / 80%);
  border: none;
  border-radius: 0px 10px 0px 10px;
  color: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.menu-item {
  padding: 5px 0px;
  cursor: pointer;
  width: 100%;
  display: flex;
  justify-content: center;
}

.menu-itemborder1 {
  border-radius: 0px 10px 0px 0px;
}

.menu-itemborder2 {
  border-radius: 0px 0px 0px 10px;
}

.menu-item:hover {
  background-color: #a2bbda;
}
相关推荐
IT女孩儿6 分钟前
JavaScript--WebAPI查缺补漏(二)
开发语言·前端·javascript·html·ecmascript
m0_748256562 小时前
如何解决前端发送数据到后端为空的问题
前端
请叫我飞哥@2 小时前
HTML5适配手机
前端·html·html5
@解忧杂货铺4 小时前
前端vue如何实现数字框中通过鼠标滚轮上下滚动增减数字
前端·javascript·vue.js
F-2H6 小时前
C语言:指针4(常量指针和指针常量及动态内存分配)
java·linux·c语言·开发语言·前端·c++
gqkmiss6 小时前
Chrome 浏览器插件获取网页 iframe 中的 window 对象
前端·chrome·iframe·postmessage·chrome 插件
m0_748247558 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
m0_748255029 小时前
前端常用算法集合
前端·算法
真的很上进9 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web130933203989 小时前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui