Vue实现右键菜单组件的详细教程,支持快捷键
vue
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
项目地址:https://gitcode.com/gh_mirrors/vu/vue
免费下载资源
·
在Web应用程序开发中,右键菜单是一个常见的功能需求。它允许用户通过鼠标右键点击元素,弹出一个自定义的菜单,提供一系列操作选项。Vue.js作为一种流行的JavaScript框架,提供了丰富的工具和组件,可以轻松实现各种交互效果,包括右键菜单。本文将向你展示如何使用Vue.js实现一个灵活可定制的右键菜单组件。
使用Vue.js的组件化开发方式来实现右键菜单组件。该组件接受一个选项数组作为参数,每个选项包含菜单项的名称、点击事件、图标和快捷键提示。当用户右键点击某个元素时,组件会根据鼠标位置显示菜单,并响应用户的点击事件。组件还支持快捷键操作,用户可以通过按下指定的组合键来触发对应的菜单项。
图片展示
一.代码说明
1.属性定义
组件代码解析: 首先,我们需要在Vue组件中定义以下数据属性:
- isContextMenuVisible:控制右键菜单的显示和隐藏。
- contextMenuStyle:用于设置右键菜单的位置。
- pressedKeys:存储按下的组合键。
- timeout:用于清除按键数组的定时器。
2.监听器定义
接下来,我们需要在组件的mounted钩子函数中添加事件监听器,分别监听keydown、keyup和click事件。这些事件用于实现右键菜单的显示、隐藏和快捷键操作。
mounted() {
window.addEventListener('keydown', this.handleKeyDown);
window.addEventListener('keyup', this.handleKeyUp);
window.addEventListener('click', this.handleClickOutside);
},
beforeUnmount() {
window.removeEventListener('keydown', this.handleKeyDown);
window.removeEventListener('keyup', this.handleKeyUp);
window.removeEventListener('click', this.handleClickOutside);
},
3.方法定义
在组件的methods中,我们定义了以下方法:
- showContextMenu(event, options):显示右键菜单。该方法接受鼠标事件对象和选项数组作为参数,并根据鼠标位置计算菜单的位置。
showContextMenu(event) {
event.preventDefault(); // 阻止默认右键菜单
this.isContextMenuVisible = true;
const menuWidth = 280 // 计算弹窗的宽度,可以根据实际情况获取
const windowWidth = window.innerWidth;
const maxLeft = windowWidth - menuWidth; // 弹窗最大允许的 left 值
let left = event.clientX;
if (left > maxLeft) {
left = maxLeft;
}
this.contextMenuStyle = {
top: `${event.clientY}px`,
left: `${left}px`
};
},
- hideContextMenu():隐藏右键菜单。
hideContextMenu() {
this.isContextMenuVisible = false;
},
- handleOptionClick(action):处理菜单项的点击事件。该方法调用传入的点击事件处理函数,并隐藏右键菜单。
handleOptionClick(action) {
this.hideContextMenu();
action(); // 执行传入的方法
},
- handleKeyDown(event):按键按下事件。该方法将按下的键值存入pressedKeys数组,并设置定时器清空该数组。
handleKeyDown(event) {
const key = event.key.toLowerCase();
if (this.pressedKeys.indexOf(key) === -1) {
this.pressedKeys.push(key)
}
},
- handleKeyUp(event):按键松开事件。该方法通过调用matchShortcut方法匹配菜单项的快捷键,并执行对应的方法。
handleKeyUp(event) {
this.matchShortcut(event);
this.pressedKeys = [];
},
- matchShortcut(event):匹配菜单项的快捷键,并执行对应的方法。
matchShortcut(event) {
for (const option of this.options) {
if (option.shortcut && this.isShortcutPressed(option.shortcutKey)) {
event.preventDefault(); // 阻止默认快捷键操作
option.action(); // 执行对应选项的方法
return;
}
}
},
- isShortcutPressed(shortcutKey):判断按下的组合键是否与菜单项的快捷键匹配。
isShortcutPressed(shortcutKey) {
const keys = shortcutKey.toLowerCase().split('+').map(key => key.trim());
if (keys.length !== this.pressedKeys.length) {
return false;
}
for (const key of keys) {
if (!this.pressedKeys.includes(key)) {
return false;
}
}
return true;
},
最后,我们还定义了一个handleClickOutside方法,用于处理点击右键菜单外部的事件,当用户点击菜单外部时,会隐藏菜单。
handleClickOutside(event) {
if (!this.$el.contains(event.target)) {
this.hideContextMenu();
}
},
4.完整代码
<template>
<div id="contextMenu" v-show="isContextMenuVisible" :style="{ top: contextMenuStyle.top, left: contextMenuStyle.left }"
class="context-menu">
<div v-for="(option, index) in options" :key="index" @click="handleOptionClick(option.action)"
class="context-menu-option">
<span class="icon">{{ option.icon }}</span> <!-- 增加图标 -->
<span>{{ option.name }}</span>
<span class="shortcut">{{ option.shortcut }}</span> <!-- 增加快捷键提示 -->
</div>
</div>
</template>
<script>
import { ref, nextTick } from 'vue'
/**
* 右键菜单组件
* options : 菜单配置信息
*
* option: { name: 菜单项名称, action: 引用组件内需要调用的事件, icon: 菜单图标 shortcut: 快捷键提示, shortcutKey: 快捷键按钮组合,特殊符号需要使用英文名称 },
* { name: '上一页', action: this.prevPage, shortcut: "Alt+向上箭头", shortcutKey: 'alt + arrowup' },
*
* 引用组件 定义方法: this.$refs.contextMenu.showContextMenu(event, this.contextMenuOption);
*
*/
export default {
props: {
options: {
type: Array,
required: true,
default: []
},
},
data() {
return {
isContextMenuVisible: false,
contextMenuStyle: {
top: '0px',
left: '0px'
},
pressedKeys: [],
timeout: null,
};
},
mounted() {
window.addEventListener('keydown', this.handleKeyDown);
window.addEventListener('keyup', this.handleKeyUp);
window.addEventListener('click', this.handleClickOutside);
},
beforeUnmount() {
window.removeEventListener('keydown', this.handleKeyDown);
window.removeEventListener('keyup', this.handleKeyUp);
window.removeEventListener('click', this.handleClickOutside);
},
methods: {
showContextMenu(event, options) {
event.preventDefault(); // 阻止默认右键菜单
this.isContextMenuVisible = true;
const menuWidth = 280 // 计算弹窗的宽度,可以根据实际情况获取
const windowWidth = window.innerWidth;
const maxLeft = windowWidth - menuWidth; // 弹窗最大允许的 left 值
let left = event.clientX;
if (left > maxLeft) {
left = maxLeft;
}
this.contextMenuStyle = {
top: `${event.clientY}px`,
left: `${left}px`
};
// this.options = options;
},
hideContextMenu() {
this.isContextMenuVisible = false;
},
handleOptionClick(action) {
this.hideContextMenu();
action(); // 执行传入的方法
},
/**
* 按键按下事件
* @param {*} event
*
* 1s内将按下的组合键装入数组,避免冲突
*
*/
handleKeyDown(event) {
this.pressedKeys.push(event.key.toLowerCase());
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.pressedKeys = [];
}, 1200);
},
/**
* 按键松开事件
* @param {*} event
*
*/
handleKeyUp(event) {
this.matchShortcut(event);
},
/**
* 用于快捷键匹配菜单项并执行相应的方法
* @param {*} event
*/
matchShortcut(event) {
for (const option of this.options) {
if (option.shortcut && this.isShortcutPressed(option.shortcutKey)) {
event.preventDefault(); // 阻止默认快捷键操作
option.action(); // 执行对应选项的方法
return;
}
}
},
/**
* 按下按键 匹配菜单项快捷键
* @param {*} shortcutKey
*/
isShortcutPressed(shortcutKey) {
const keys = shortcutKey.toLowerCase().split('+').map(key => key.trim());
if (keys.length !== this.pressedKeys.length) {
return false;
}
for (const key of keys) {
if (!this.pressedKeys.includes(key)) {
return false;
}
}
return true;
},
handleClickOutside(event) {
if (!this.$el.contains(event.target)) {
this.hideContextMenu();
}
},
},
};
</script>
<style>
.context-menu {
position: fixed;
z-index: 1000;
min-width: 150px;
max-width: 300px;
background-color: white;
border: none;
border-radius: 3px;
box-shadow: 0 0 5px #ccc;
}
/* .context-menu-option {
height: 30px;
font-size: 14px;
padding: 5px 5px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
} */
.context-menu-option {
display: flex;
font-size: 12px;
align-items: center;
justify-content: center;
padding: 10px 5px;
cursor: pointer;
}
.context-menu-option:not(:last-child) {
border-bottom: 1px solid #eee;
}
.icon {
margin-right: 20px;
/* 控制图标与文字之间的间距 */
}
.shortcut {
margin-right: 10px;
margin-left: 40px;
/* 将快捷键提示放置在右侧 */
text-align: right;
/* 文字靠右显示 */
}
.context-menu-option:hover {
background-color: #f0f0f0;
}
</style>
二. 组件使用
1. 取消默认监听器
页面需要添加监听器取消浏览器默认的右键菜单
// 在页面加载时添加事件监听器
document.addEventListener('contextmenu', function (event) {
event.preventDefault(); // 取消默认的右键菜单行为
});
2.页面引用
使用右键菜单组件: 要在你的Vue项目中使用右键菜单组件,需要完成以下步骤:
- 将上述代码保存为一个名为ContextMenu.vue的组件文件。
- 在需要使用右键菜单的组件中,引入ContextMenu组件并注册。
- 在data属性中定义一个选项数组,包含所有菜单项的配置信息。
- 在需要触发右键菜单的元素上,添加@contextmenu事件,调用showContextMenu方法显示菜单。
<template>
<div>
<!-- 此处为触发右键菜单的元素 -->
<div @contextmenu="handleRightClick">
右键点击我
</div>
<!-- 引入ContextMenu组件 -->
<ContextMenu ref="contextMenu" :options="options" />
</div>
</template>
<script>
import ContextMenu from './ContextMenu.vue';
// 在页面加载时添加事件监听器
document.addEventListener('contextmenu', function (event) {
event.preventDefault(); // 取消默认的右键菜单行为
});
export default {
components: {
ContextMenu,
},
data() {
return {
contextMenuOption: [ // 右键菜单选项,shortcutKey需要按键的英文名称 ...
{ name: '上一页', action: this.prevPage, shortcut: "Alt+向上箭头", shortcutKey: 'alt + arrowup' },
{ name: '下一页', action: this.nextPage, shortcut: "Alt+向下箭头", shortcutKey: 'alt + arrowdown' },
],
};
},
methods: {
handleRightClick(event) {
this.$refs.contextMenu.showContextMenu(event, this.contextMenuOption);
},
}
};
</script>
这个组件提供了灵活的配置选项,可以满足不同场景下的需求。可以根据自己的项目需求进行定制和扩展
GitHub 加速计划 / vu / vue
207.54 K
33.66 K
下载
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:2 个月前 )
73486cb5
* chore: fix link broken
Signed-off-by: snoppy <michaleli@foxmail.com>
* Update packages/template-compiler/README.md [skip ci]
---------
Signed-off-by: snoppy <michaleli@foxmail.com>
Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 4 个月前
e428d891
Updated Browser Compatibility reference. The previous currently returns HTTP 404. 5 个月前
更多推荐
已为社区贡献1条内容
所有评论(0)