这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

下图这个情景,你是否也遇到过?

当你右键点击网页上的某个元素时,弹出的菜单被屏幕边缘遮挡了,导致你无法看清或选择菜单项?

上图中右键菜单的选项并不是固定不变的,它会根据不同的元素或场景来显示不同的选项。

也就是说,菜单的内容和大小都是动态生成的,而不是预先设定好的。

这就给我们调整菜单位置带来了一定的难度,不过当你看完这篇文章所有的问题都不再是问题。

分析问题

遇事不决先画图,我们要解决的问题本质上就是菜单生成的位置,所以我们画个图来找一下头绪:

我们通过上图可以知道,菜单能否在视口中放得下,取决于两个条件:

  1. windowW(视口宽度) - mouseX(鼠标 x 坐标) > menuW(菜单宽度)
  2. 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 右键菜单的秘密:自适应位置的实现方法的更多相关文章

  1. atitit。wondows 右键菜单的管理与位置存储

    atitit.wondows 右键菜单的管理与位置存储 原理 .这样的功能称为Windows外壳扩展(Shell Extensions) 1 常用右键菜单 atiContentMenu1 通用tool ...

  2. vue 右键菜单插件 简单、可扩展、样式自定义的右键菜单

    今天分享的不是技术,今天给大家分享个插件,针对现有的vue右键菜单插件,大多数都是需要使用插件本身自定义的标签,很多地方不方便,可扩展性也很低,所以我决定写了一款自定义指令调用右键菜单(vuerigh ...

  3. Vue右键菜单

    rightShow(item) { this.isPersoncontextMenus = true; let menu = document.getElementById("msgRigh ...

  4. window注册鼠标右键菜单,及子菜单

    最近项目中要用到c#并且要注册鼠标点击右键菜单,在这里总结了几种方法以便记录 效果图: 1,reg注册,创建.reg文件,内容如下 Windows Registry Editor Version 5. ...

  5. windows下命令行利器---Cmder(安装,中文乱码,配置右键菜单)

    很多人都是在win下开发的,这样就会出现,经常需要命令行操作,而win cmd命令和linux命令有很大差异,导致大家很难受,今天给大家介绍一个win下命令行的利器-Cmder 一.先看一下它的容颜 ...

  6. vue+element树形结构右键菜单

    环境:vue-admin-template   vue 2.6.10 element-ui 2.7.0 1.自定义组件,文件位置:src/components/mentContext <temp ...

  7. 基于electron+vue+element构建项目模板之【自定义标题栏&右键菜单项篇】

    1.概述 开发平台OS:windows 开发平台IDE:vs code 本篇章将介绍自定义标题栏和右键菜单项,基于electron现有版本安全性的建议,此次的改造中主进程和渲染进程彼此语境隔离,通过预 ...

  8. 【转】windows7的桌面右键菜单的“新建”子菜单,在注册表哪个位置,如何在“新建"里面添加一个新项

    点击桌面,就会弹出菜单,然后在“新建”中就又弹出可以新建的子菜单栏.office与txt 的新建都是在这里面的.我想做的事情是:在右键菜单的“新建” 中添加一个“TQ文本”的新建项,然后点击它之后,桌 ...

  9. Jquery 右键菜单(ContextMenu)插件使用记录

    目前做的项目需要在页面里面用右键菜单,在网上找到两种jquery的右键菜单插件,但是都有各种问题.所以就自己动手把两种插件结合了下. 修改后的右键菜单插架可以根据绑定的触发页面元素不同,复用同一个菜单 ...

  10. Vue 2.0 右键菜单组件 Vue Context Menu

    Vue 2.0 右键菜单组件 Vue Context Menu https://juejin.im/entry/5976d14751882507db6e839c

随机推荐

  1. 推荐一款id生成器: Hashids

    唯一 id 生成的方式有很多种,比较常见的有以下几种方式: 语言自带功能,如 Java 中的 UUID,常用于后端 第三方工具提供,如 npm 中的 nanoid,常用于前端 Twitter 开源的 ...

  2. LGV引理

    LGV引理是用来统计DAG中固定若干起点和终点情况下的选择不相交链的方案数的. 同样用来优化计数问题,但是比Pólya定理友好多了,这也就是为什么它能够被直接糊到NOI考场上. 对于一张DAG,每条边 ...

  3. 西门子SIMATIC LPMLV30 库的模式和状态管理器

    从基于S7-1200 / S7-1500的OMAC PackML V3.0获取到的文章内容,用于记录查看 基本信息 根据PackML_V3.0,该库包含了用于机械模式和状态管理器的功能模块. • 机械 ...

  4. python-鼠标宏

    按下鼠标左键, 连击 按下鼠标右键, 停止 import win32api import time from pynput.mouse import Button, Controller mouse ...

  5. Q查询的高级用法

    示例:如前端需要通过下拉框选择需要通过什么过滤字段来查询输入的关键字,后端如何使用Q查询过滤包含输入的关键字呢? def customers(request): search_field = requ ...

  6. 【LeetCode双指针】合并两个有序数组,从后向前遍历

    合并两个有序数组 https://leetcode.cn/problems/merge-sorted-array/ 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m ...

  7. 【LeetCode贪心#04】跳跃游戏I + II

    跳跃游戏 力扣题目链接(opens new window) 给定一个非负整数数组,你最初位于数组的第一个位置. 数组中的每个元素代表你在该位置可以跳跃的最大长度. 判断你是否能够到达最后一个位置. 示 ...

  8. 我的第一个项目(十三) :组件间传值的一些方案(vuex,eventbus,localStorage)

    好家伙,   先说一下我的需求,我要组件间传值 1.eventBus 前端兄弟组件传值eventbus无法使用 不报错也不触发,就很奇怪 //eventBus.js import Vue from & ...

  9. 百度爱番番基于图技术、流式计算的实时CDP建设实践

    导读:随着营销3.0时代的到来,企业愈发需要依托强大CDP能力解决其严重的数据孤岛问题,帮助企业加温线索.促活客户.但什么是CDP.好的CDP应该具备哪些关键特征?本文在回答此问题的同时,详细讲述了爱 ...

  10. Java this关键字使用 详解+ 证明

    1 package com.bytezero.thistest; 2 /** 3 * 4 * @Description 5 * @author Bytezero·zhenglei! Email:420 ...