取消原生右键事件

在 main.ts 函数中取消浏览器默认右键菜单:

window.oncontextmenu = () => {  return false;};

组件模板

做一个不同区域右键点击之后不同菜单项的组件,创建组件模板:

模板中使用了 Teleport,需要把这一块传递到页面中 l-menu-container 元素中去,这个元素在 body 下,意思是我的右键菜单组件呼出的面板不受父组件的样式影响,包括移动的范围限制、背景颜色等,这些继承父元素样式都需要避免。

组件 setup

需要获取组件模板的一些引用:

// ContextMenu setupconst menu = ref();const head = ref();const panel = ref();const milliseconds = new Date().getMilliseconds();const { x, y } = useDraggable(head);onMounted(() => {  menu.value.onmouseup = e => {    if (e.button == 2) {      const container = document.querySelector("#l-menu-container");      const menuId = container.getAttribute("menu-id");      menuId && (document.getElementById(`l-menu__panel__${menuId}`).style.display = "none");      container.setAttribute("menu-id", `${milliseconds}`);      panel.value.style.left = `${e.clientX}px`;      panel.value.style.top = `${e.clientY}px`;      panel.value.style.display = "block";    }  };});

鼠标按下放开事件,判断鼠标是左键还是右键,button 为 2 代表右键,右键点击之后需要移除上一个开启的面板,在 body 下的 #l-menu-container 元素上添加一个记录上一次面板的 id。

实现右键出来之后的菜单面板自由地在窗口中移动,直接借助 VueUse useDraggable 函数:

const { x, y } = useDraggable(head);

得到鼠标移动的 x 和 y 值,通过绑定 style 对 left 和 top 进行设置,就可以实现自由移动。

使用组件

该组件有三个插槽,一个默认插槽,两个具名插槽。具名 content 插槽是菜单内容,默认插槽是能呼出右键菜单面板的区域,哪个区域能呼出就在外面套一层 ContextMenu 组件:

  
hello 样式设置

实现效果