记录--Vue 右键菜单的秘密:自适应位置的实现方法
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
下图这个情景,你是否也遇到过?
当你右键点击网页上的某个元素时,弹出的菜单被屏幕边缘遮挡了,导致你无法看清或选择菜单项?
上图中右键菜单的选项并不是固定不变的,它会根据不同的元素或场景来显示不同的选项。
也就是说,菜单的内容和大小都是动态生成的,而不是预先设定好的。
这就给我们调整菜单位置带来了一定的难度,不过当你看完这篇文章所有的问题都不再是问题。
分析问题
遇事不决先画图,我们要解决的问题本质上就是菜单生成的位置,所以我们画个图来找一下头绪:
我们通过上图可以知道,菜单能否在视口中放得下,取决于两个条件:
windowW(视口宽度) - mouseX(鼠标 x 坐标) > menuW(菜单宽度)
windowH(视口高度) - mouseY(鼠标 y 坐标) > menuH(菜单高度)
当同时满足这两个条件的时候说明菜单放得下,那我们就要思考如果不满足条件的时候怎么办了。
如果不满足条件一说明宽度放不下,那我们就让菜单生成到鼠标的左边 mouseX - menuW
,就像下图这样。
如果不满足条件二说明高度放不下,那我们就让菜单贴底 windowH - menuH
,像这样。
那如果两个条件都不满足,就同时应用两个解决办法。
解决问题
先来看一下现在的代码:
<template>
<div ref="containerRef">
<slot></slot>
<Teleport to="body">
<div v-if="showMenu" class="context-menu" :style="{
left: mouseX + 'px',
top: mouseY + 'px',
}">
<div class="menu-list">
<div @click="handleClick(item)" class="menu-item" v-for="(item, i) in menu" :key="item.label">
{{ item.label }}
</div>
</div>
</div>
</Teleport>
</div>
</template>
<script setup>
import { ref } from 'vue';
import useContextMenu from './useContextMenu';
const props = defineProps({
menu: {
type: Array,
default: () => [],
},
});
const containerRef = ref(null);
const { mouseX, mouseY, showMenu } = useContextMenu(containerRef); function handleClick() {
showMenu.value = false;
}
</script>
看到我们现在是直接将鼠标的坐标赋值给了菜单,那么接下来就要给菜单一个经过计算的合适位置。
我们知道视口的大小、鼠标的位置、菜单的大小都是会变化的,所以这几个数据都要是响应式。
现在仅仅知道鼠标的位置,还需要知道视口与菜单的大小。
视口大小我们写一个函数来监听视口大小的变化:
import { ref } from "vue";
const windowW = ref(document.documentElement.clientWidth);
const windowH = ref(document.documentElement.clientHeight); window.addEventListener("resize", () => {
windowW.value = document.documentElement.clientWidth;
windowH.value = document.documentElement.clientHeight;
}); export default function () {
return {
windowW,
windowH,
};
}
而菜单的大小可以利用之前写过的一个自定义指令来监听菜单大小的变化,代码如下:
const map = new WeakMap();
const ob = new ResizeObserver((entries) => {
for (const entry of entries) {
// 这个元素对应的回调函数?
const handler = map.get(entry.target);
if (handler) {
const box = entry.borderBoxSize[0];
handler({
width: box.inlineSize,
height: box.blockSize,
});
}
}
}); export default {
mounted(el, binding) {
// 监视尺寸变化
ob.observe(el);
map.set(el, binding.value);
},
unmounted(el) {
// 取消监听
ob.unobserve(el);
},
};
现在这些值我们都已经知道了,我们去实现一下。
<template>
<div ref="containerRef">
<slot></slot>
<Teleport to="body">
<!-- 将计算好的位置赋值给菜单 -->
<div v-if="showMenu" class="context-menu" :style="{
left: pos.posX + 'px',
top: pos.posY + 'px',
}">
<!-- 指令为全局指令,在菜单上使用指令来监听菜单尺寸的变化并触发函数 -->
<div v-size-ob="handleSize" class="menu-list">
<div @click="handleClick(item)" class="menu-item" v-for="(item, i) in menu" :key="item.label">
{{ item.label }}
</div>
</div>
</div>
</Teleport>
</div>
</template> <script setup>
import { ref } from 'vue';
import useContextMenu from './useContextMenu';
import { computed } from '@vue/reactivity';
// 引入监听视口大小的函数
import useViewport from './useViewport';
const props = defineProps({
menu: {
type: Array,
default: () => [],
},
});
const containerRef = ref(null);
const { mouseX, mouseY, showMenu } = useContextMenu(containerRef); // 声明两个响应式变量,用来记录菜单大小的变化。
const menuW = ref(0);
const menuH = ref(0);
function handleSize({ width, height }) {
menuW.value = width;
menuH.value = height;
}
// 获得视口的大小
const { windowW, windowH } = useViewport();
// 计算属性,用来计算菜单合适的位置
const pos = computed(() => {
let posX = mouseX.value;
let posY = mouseY.value;
// 宽度放不下生成新的位置
if (mouseX.value > windowW.value - menuW.value) {
posX = mouseX.value - menuW.value
}
// 高度放不下生成新的位置
if (mouseY.value > windowH.value - menuH.value) {
posY = windowH.value - menuH.value
}
return {
posX,
posY,
};
}); function handleClick() {
showMenu.value = false;
}
</script>
我们现在来看一下效果如何。
效果完美!
总结
这样,我们就实现了一个简单的右键菜单,它可以根据鼠标的位置和视口的大小自动调整菜单的位置,避免被遮挡。
这个功能虽然看起来不起眼,但是却能提高用户的体验和操作的便捷性。
当然,这个功能还有很多可以改进的地方,比如菜单的样式、动画、交互等等。
本文转载于:
https://juejin.cn/post/7250284380231712828
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。
记录--Vue 右键菜单的秘密:自适应位置的实现方法的更多相关文章
- atitit。wondows 右键菜单的管理与位置存储
atitit.wondows 右键菜单的管理与位置存储 原理 .这样的功能称为Windows外壳扩展(Shell Extensions) 1 常用右键菜单 atiContentMenu1 通用tool ...
- vue 右键菜单插件 简单、可扩展、样式自定义的右键菜单
今天分享的不是技术,今天给大家分享个插件,针对现有的vue右键菜单插件,大多数都是需要使用插件本身自定义的标签,很多地方不方便,可扩展性也很低,所以我决定写了一款自定义指令调用右键菜单(vuerigh ...
- Vue右键菜单
rightShow(item) { this.isPersoncontextMenus = true; let menu = document.getElementById("msgRigh ...
- window注册鼠标右键菜单,及子菜单
最近项目中要用到c#并且要注册鼠标点击右键菜单,在这里总结了几种方法以便记录 效果图: 1,reg注册,创建.reg文件,内容如下 Windows Registry Editor Version 5. ...
- windows下命令行利器---Cmder(安装,中文乱码,配置右键菜单)
很多人都是在win下开发的,这样就会出现,经常需要命令行操作,而win cmd命令和linux命令有很大差异,导致大家很难受,今天给大家介绍一个win下命令行的利器-Cmder 一.先看一下它的容颜 ...
- vue+element树形结构右键菜单
环境:vue-admin-template vue 2.6.10 element-ui 2.7.0 1.自定义组件,文件位置:src/components/mentContext <temp ...
- 基于electron+vue+element构建项目模板之【自定义标题栏&右键菜单项篇】
1.概述 开发平台OS:windows 开发平台IDE:vs code 本篇章将介绍自定义标题栏和右键菜单项,基于electron现有版本安全性的建议,此次的改造中主进程和渲染进程彼此语境隔离,通过预 ...
- 【转】windows7的桌面右键菜单的“新建”子菜单,在注册表哪个位置,如何在“新建"里面添加一个新项
点击桌面,就会弹出菜单,然后在“新建”中就又弹出可以新建的子菜单栏.office与txt 的新建都是在这里面的.我想做的事情是:在右键菜单的“新建” 中添加一个“TQ文本”的新建项,然后点击它之后,桌 ...
- Jquery 右键菜单(ContextMenu)插件使用记录
目前做的项目需要在页面里面用右键菜单,在网上找到两种jquery的右键菜单插件,但是都有各种问题.所以就自己动手把两种插件结合了下. 修改后的右键菜单插架可以根据绑定的触发页面元素不同,复用同一个菜单 ...
- Vue 2.0 右键菜单组件 Vue Context Menu
Vue 2.0 右键菜单组件 Vue Context Menu https://juejin.im/entry/5976d14751882507db6e839c
随机推荐
- NC20568 [SCOI2012]滑雪与时间胶囊
题目链接 题目 题目描述 a180285非常喜欢滑雪.他来到一座雪山,这里分布着M条供滑行的轨道和N个轨道之间的交点(同时也是景点),而且每个景点都有一编号i(1 ≤ i ≤ N)和一高度Hi.a18 ...
- 每月免费调用1000次API调用:实现PDF转档、页面编辑、OCR
每月1000次免费PDF API调用: 使用ComPDFKit API充分发挥您PDF转换的全部潜力 您是否在寻找无需前期投资即可提升软件集成能力的途径?再也不用找了!我们先进的API为您的项目提供所 ...
- Python OS.mkdirs与OS.makedirs的区别
os.mkdir只创建最外层目录,如果创建多级目录,报错"FileNotFoundError: [WinError 3] 系统找不到指定的路径".目录存在报错. os.makedi ...
- 解决:This system is not registered to Red Hat Subscription Management
使用yum命令安装软件时候出现以下错误: This system is not registered to Red Hat Subscription Management.You can use su ...
- python第二章pta习题总结
chapter2 一.选择填空判断部分 1. C语言中,变量变的是内存空间中的值,不变的是地址: Python中,变量变的是地址,不变的是内存空间中的值. 2. 3.bool('False') 的返回 ...
- std::string std::wstring char w_char 内部中文编码
最近在处理一个字符串转码问题,故记录一下过程 该需求是外部 sdk 的一个 api 需要一个 char* 字符串路径入参,我以往是将宽字符串转为 UTF8 后再传给 sdk 这次这个 api 似乎不接 ...
- 【Android 逆向】【攻防世界】easy-dex
这一题不easy,不知为何叫这个名字.... 1. apk 安装到手机,不知所云,各种亮瞎眼闪光 2. jadx 打开apk,一行java代码都没有,打开AndroidManifest看看 <? ...
- Golang条件编译介绍
相信熟悉 Golang 的小伙伴不少都知道 条件编译 这个事,最近项目中也可能会用到这个东西.所以特意重新学习下,记录下学习的过程.这样用的时候记不住了,还可以直接过来看自己的笔记. 文章很多内容来源 ...
- 【Azure 应用服务】遇见“无法创建hybrid connection for App Service”的解决办法
Hybrid Connection (混合连接) 在两个联网应用程序之间启用了双向.请求-响应和二进制流通信以及简单的数据报流.通过混合连接,可以实现应用部署在公网环境中,而数据库保存在本地私网环境中 ...
- 2023 年值得一读的技术文章 | NebulaGraph 技术社区
在之前的产品篇,我们了解到了 NebulaGraph 内核及周边工具在 2023 年经历了什么样的变化.伴随着这些特性的变更和上线,在[文章]博客分类中,一篇篇的博文记录下了这些功能背后的设计思考和研 ...