千家信息网

如何实现react版模拟亚马逊人机交互菜单

发表于:2026-11-04 作者:千家信息网编辑
千家信息网最后更新 2026年11月04日,小编给大家分享一下如何实现react版模拟亚马逊人机交互菜单,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!需求介绍本文都是在web端的需求参考亚马逊和京东商城的首页左侧菜单效果,实现
千家信息网最后更新 2026年11月04日如何实现react版模拟亚马逊人机交互菜单

小编给大家分享一下如何实现react版模拟亚马逊人机交互菜单,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!

    需求介绍

    本文都是在web端的需求

    参考亚马逊和京东商城的首页左侧菜单效果,实现一个react版本的组件,以供业务使用。

    我们先看下亚马逊和京东商城的效果:

    亚马逊商城

    京东商城

    从上面的效果得出我们的菜单效果需求点:

    • 当我们的鼠标悬浮在左侧菜单的时候,右侧会对应展示它对应的子菜单项,

    • 当我们的鼠标在左侧菜单上下移动时,左侧可以快速切换为对应的子菜单

    • 当我们的鼠标移动以一定的倾斜角度移动到右侧的时候,鼠标虽然会经过其它的左侧菜单,但是不会执行切换。

    到目前为止,我们就搞情况了我们的需求。接下来就要去实现我们的方案了。

    实现方案

    要实现我们的需求,复杂点主要是在如何实现上述的需求3。需求1和需求2 的基本切换效果我就不再说了,直接进入需求3

    实现需求3

    如果要实现这个需求,我们需要记录鼠标从左往右(从左侧菜单区域移动到右侧菜单区域)的移动轨迹,然后根据它的移动轨迹去判断它是否是在一个三角形的区域之内,如果在的话,就不让它切换菜单。

    我们先看一张图:

    • P1:鼠标的起始位置

    • P2:左侧菜单的固定点1,鼠标在左侧区域的最大位移点

    • P3:左侧菜单的固定点2,鼠标在左侧区域的最大位移点

    • M:鼠标在左侧菜单移动的结束位置

    从上图我们可以得出:

    如果鼠标的起始点是在 P1 的话,当鼠标移动到右侧区域,鼠标可能经过的三角形区域就是 P1-P2-P3所在的三角形,M点是鼠标的结束位置。所以我们判断鼠标的运动轨迹是否在三角形中就可以了。

    部分逻辑代码

    const [active, setActive] = useState(null) // 选中的菜单  const [showSub, setShowSub] = useState(false) // 是否显示子菜单  let timeout = useRef(null) //  设置延迟定时器,防止鼠标移到tab内容经过菜单时的切换  let mouseLocs = useRef([]) // 记录鼠标移动时的坐标数组  let firstSlope = useRef(null) // 菜单栏的固定点1, 根据菜单栏和内容的位置而改变  let secondSlope = useRef(null) // 菜单栏的固定点2, 根据菜单栏和内容的位置而改变  const refNavigation = useRef(null)  const refNav = useRef(null)  const refSubnav = useRef(null)  /**   * 根据内容栏相对于菜单栏的位置,判断移动过程中的点是否在三角形内   * @param {Object} p1 开始位置   * @param {Object} p2 菜单栏固定点1   * @param {Object} p3 菜单栏固定点2   * @param {Object} m 结束位置   * @return {*}   */  function proPosInTriangle(p1, p2, p3, m) {    // 结束时鼠标坐标位置    let x = m.x,      y = m.y,      // 开始鼠标坐标位置      x1 = p1.x,      y1 = p1.y,      // 菜单栏包裹层右上角坐标      x2 = p2.x,      y2 = p2.y,      // 右下角坐标      x3 = p3.x,      y3 = p3.y,      // (y2 - y1) / (x2 - x1)为两坐标连成直线的斜率      // 因为直线的公式为y=kx+b;当斜率相同时,只要比较      // b1和b2的差值就可以知道该点是在      // (x1,y1),(x2,y2)的直线的哪个方向      // 当r1大于0,说明该点在直线右侧,其它以此类推      r1 = y - y1 - ((y2 - y1) / (x2 - x1)) * (x - x1),      r2 = y - y2 - ((y3 - y2) / (x3 - x2)) * (x - x2),      r3 = y - y3 - ((y1 - y3) / (x1 - x3)) * (x - x3),      compare    compare = r1 * r2 * r3 < 0 && r1 > 0    // 返回是否在三角形内的结果    return compare  }  /**   * 获取元素相对于浏览器左上角的坐标位置,为正值   * @param element   * @return {{x: Number, y: Number}}   * @constructor   */  function LocFromdoc(element) {    const { x, y, width, height } = element.getBoundingClientRect()    return {      x: x,      y: y,      width,      height,    }  }  /**   * 记录元素的位置信息   * @param element   * @return {{top: *, topAndHeight: number, left: *, leftAndWidth: number}}   */  function getInfo(element) {    const location = LocFromdoc(element)    return {      top: location.y,      topAndHeight: location.y + element.offsetHeight, // offsetHeight 元素的像素高度, 高度包含该元素的垂直内边距和边框,且是一个整数      left: location.x,      leftAndWidth: location.x + element.offsetWidth,    }  }  /**   * 根据内容栏相对于菜单栏的位置, 返回菜单栏的固定点1,和固定点2,保存在this.firstSlope和this.secondSlope对象里   * 即 左侧菜单栏的右上角和右下角的位置   */  function ensureTriangleDots() {    // 获取菜单栏的位置    const info = getInfo(refNav.current)    const x1 = info.leftAndWidth    const y1 = info.top    const x2 = x1    const y2 = info.topAndHeight    firstSlope.current = {      x: x1,      y: y1,    }    secondSlope.current = {      x: x2,      y: y2,    }  }  const onMouseOver = useCallback(    obj => {      let diff      try {        // 是否在指定三角形内        diff = proPosInTriangle(          mouseLocs.current[0],          firstSlope.current,          secondSlope.current,          mouseLocs.current[2]        )      } catch (ex) {}      // 是就启动延迟显示,      // 否则不延迟      if (diff) {        timeout.current = setTimeout(() => {          setActive(obj.key)          setShowSub(true)        }, 300)      } else {        setActive(obj.key)        setShowSub(true)      }    },    [mouseLocs, timeout]  )  const onMouseEnter = () => {    // 计算位置    if (refNav.current) {      ensureTriangleDots()    }    setShowSub(true)  }  // 移出菜单所在区域  const onMouseLeave = () => {    if (refSubnav.current) {      setActive(null)      setShowSub(false)    }  }  // 记录鼠标在菜单栏中移动的最后三个坐标位置  const onMousemove = event => {    mouseLocs.current.push({      x: event.pageX,      y: event.pageY,    })    if (mouseLocs.current.length > 3) {      // 移除超过三项的数据      mouseLocs.current.shift()    }  }  // 鼠标移出的时候,清除延时器  const onMouseout = () => {    if (timeout.current) {      clearTimeout(timeout.current)    }  }

    实现效果

    看完了这篇文章,相信你对"如何实现react版模拟亚马逊人机交互菜单"有了一定的了解,如果想了解更多相关知识,欢迎关注行业资讯频道,感谢各位的阅读!

    菜单 鼠标 位置 需求 移动 区域 固定点 坐标 三角形 三角 效果 亚马 亚马逊 内容 右侧 是在 切换 元素 商城 直线 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 广东万易互联网科技有限公司惠州分公司 科技与互联网发展 奇酷互联网科技公司 沃里互联网科技 中国互联网金融科技基金 中正互联网科技招聘 云创客互联网科技公司 广西互联网科技孵化中心 互联网企业科技类企业如何做账 启辰互联网科技 全球科技与互联网公司市值变化 广州智享互联网科技 中国科技和美国互联网 保山互联网科技靠谱吗 互联网的科技体现在哪里 承德科技产业互联网平台 广东猫王互联网科技有限公司电话 东风日产互联网科技公司叫什么 前海云享互联网科技 广州全民伙伴互联网科技有限公司 小米移动互联网科技园 西安纸贵互联网科技有限公司 与互联网科技公司 致骋互联网科技 成都互联网科技公司 汇久创客互联网科技 互联网科技汛 美国互联网科技公司 玉溪新华互联网科技怎么选 广东猫王互联网科技有限公司电话
    0