首页 > 其他分享 >记录--Vue 右键菜单的秘密:自适应位置的实现方法

记录--Vue 右键菜单的秘密:自适应位置的实现方法

时间:2023-10-07 18:22:54浏览次数:56  
标签:视口 Vue const -- value 菜单 右键 ref windowH

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

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

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

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

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

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

分析问题

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

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

  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,const,--,value,菜单,右键,ref,windowH
From: https://www.cnblogs.com/smileZAZ/p/17747142.html

相关文章

  • 2023-2024-1 学号20231329 《计算机基础与程序设计》第2周学习总结
    这个作业属于哪个课程https://edu.cnblogs.com/campus/besti/2023-2024-1-CFAP这个作业的要求在哪里https://www.cnblogs.com/rocedu/p/9577842.html#WEEK02这个作业的目标计算机科学概论第1章并完成云班课测试《C语言程序设计》第1章并完成云班课测试作业正......
  • 【JAVA】算法
    start 1.SHA-256算法(单向、验证完整性/一致性,暂时安全)1importjava.nio.charset.StandardCharsets;2importjava.security.MessageDigest;3importjava.security.NoSuchAlgorithmException;45publicclassSHA256Example{67publicstaticStringhas......
  • 2023-2024-1 20231320 《计算机基础与程序设计》第二周学习总结
    2023-2024-120231320《计算机基础与程序设计》第二周学习总结作业信息这个作业属于哪个课程<班级的链接>(2023-2024-1-计算机基础与程序设计)这个作业要求在哪里<作业要求的链接>(2022-2023-1计算机基础与程序设计第一周作业)这个作业的目标<自学《计算机基础与......
  • 手写PCA(主元分析法)计算点云法向量(详细注释) 【Matlab代码】
    原理PCA原理主元分析法PCA学习笔记点云法向量与点云平面拟合的关系(PCA)EstimatingSurfaceNormalsinaPointCloud3D【24】PCA点云法向量估计利用PCA计算点云的法线3D点云法向量估计(最小二乘拟合平面)为什么用PCA做点云法线估计?利用PCA求点云的法向量pca_demo.mclcclearclosea......
  • css 样式一
    1.使用css实现箭头图标:①.一个div盒子,有border值.②.border占满所有内容区,盒子是实心的.只剩4个边框.③.将div的宽度和高度设置为0,再设置边框样式:.triangle{width:0;height:0;border-top:50pxsolidblack;border-right:50pxsolidr......
  • 代理IP与Socks5代理在技术世界的多元应用
    在数字化时代,网络工程师的任务不仅是维护网络的稳定性,还需要应对各种技术挑战。代理IP与Socks5代理作为技术工具箱中的两把利器,在跨界电商、爬虫、出海业务、网络安全和游戏领域中发挥了关键作用。本文将深入探讨这两项技术在不同领域的多元应用。1.跨界电商:解锁全球市场的通行证......
  • 基于android的中医体质的社区居民健康管理系统-计算机毕业设计源码+LW文档
    摘要首先,论文一开始便是清楚的论述了系统的研究内容。其次,剖析系统需求分析,弄明白“做什么”,分析包括业务分析和业务流程的分析以及用例分析,更进一步明确系统的需求。然后在明白了系统的需求基础上需要进一步地设计系统,主要包罗软件架构模式、整体功能模块、数据库设计。本......
  • 【93】基于java的校园课程作业管理系统设计与实现
    本章节给大家带来一个基于java校园课程作业管理系统设计与实现,可适用于班级管理、学生管理、教师管理、课程管理、课程信息管理、学生选课管理、作业布置管理、作业提交管理、作业评分管理、课程评价管理、课程资源管理,作业管理系统,大学提交作业,布置作业管理系统,学校作业管理系统......
  • 基于springboot的小程序的高校后勤管理系统-计算机毕业设计源码+LW文档
    1、选题背景与意义(含国内外相关研究综述及评价)近年来,随着计算机的不断发展和深入到各个行业中并起到了很重要的作用,给人们带来了很大的便利。在这样的趋势下,高校的后勤管理显得也很重要。在《高校后勤管理系统的设计与实现》中也提到,教育的普及和日益激烈的资源竞争,对学校的教学质......
  • #记事# 家庭网络更新
    最开始的阶段还记得刚搬来的时候,我还是个初二的学生,用的电信20M宽带,那个时候是电信配发了带WiFi的光猫,卧室里面可以说是几乎没有信号,客厅的电视都会偶尔卡顿,不过那个时候我也没手机影响不到我。高中阶段每天无所事事,自己网上找工具破解了超级密码,高二左右自己靠服务器收入买了......