本文最初发表在我的简书上
在上篇导航优化方案预判用户行为 (一) 中讲到, 我们对导航的切换操作设置了延时, 解决了移入子菜单时不执行导航切换的问题但同时引入了新的问题, 即导航的延时造成了切换不流程
如何才能做到, 即保证用户进入子菜单的便捷, 又能保证用户切换主导航的流程呢?
不防做这样的假设, 如图
其中图中的 O 点为鼠标的起始位置
若鼠标移动路径位于阴影内部, 就认为用户想要移动到当前子菜单, 如路径 O->p1
若鼠标移动路径位于阴影外部, 就认为用户想要切换菜单, 如路径 O->p2
于是问题就转化成了, 判断鼠标移动的方向是否位于阴影部分内部, 如何判断一个点位于一个三角形的内部(P 点位于三角形 0AB 内部), 具体实现则用到了数学上的向量积问题
数学思路: 判断一个点是否位于一个三角形的内部
同向法: 沿着三角形三边走一圈 A>B>C>A, 在 AB 边时, 点 P 和 C 点在同侧; 在 BC 边时, 点 P 和 A 点在同侧; 在 CA 边时, 点 P 和 B 点在同侧; 如果点 P 始终个第三个点保持同侧, 则说明该点位于三角形内; 若某一时刻两点位于两侧, 那么该点在三角形外
那么问题又转化成了, 判断两个点是否位于一条线段的同侧
这会儿就能使用向量积了
如图, 将向量 PO 和向量 OA 做叉乘, 再将向量 BO 和 OA 做叉乘, 如果两个叉乘的结果符号相同, 说明两个点位于 OA 的同一侧
- // 向量积公式
- OA×PO = (p.xa.x)(p.ya.y)(p.ya.y)(p.xa.x);
对三边都进行判断, 判断函数
- function flag(OA, PO, BO) {
- var flag = -1;
- var a = OA×PO;
- var b = OA×BO;
- if (a * b < 0) {
- //B,P 两点不同向
- flag = -1;
- } else {
- //B,P 两点同向
- flag = 1;
- }
- return flag
- }
- var x = flag(OA, PO, BO);
- var y = flag(AB, PA, OA);
- var z = flag(BO, PB, AB);
- if (x == 1 && y == 1 && z == 1) {
- // 点在三角形内部
- // 用户想进入子菜单
- // 菜单切换延迟
- } else {
- // 点在三角形外部
- // 用户想切换菜单
- // 菜单切换不延迟
- }
最终实现代码
- // 获取四个点的坐标
- var topLeft = {
- x: sub - nav.offset().left,
- y: sub - nav.offset().top
- }
- var bottomLeft = {
- x: sub - nav.offset().left,
- y: sub - nav.offset().top + sub - nav.height()
- }
- //O 点为鼠标起始坐标, P 点设置为鼠标移动后的第二个点
- var mouseTrack = []; // 跟踪鼠标坐标数组
- $("#main-nav").on('mousemove', moveHanlder) // 监听鼠标移动事件
- var moveHanlder = function(e) {
- mouseTrack.push({
- x: e.pageX,
- y: e.pageY
- });
- if (mouseTrack.length > 3) {
- // 取鼠标经过的 2 个点即可
- mouseTrack.shift();
- };
- }
- var O = mouseTrack[0];
- var P = mouseTrack[1];
到目前为止已获取了四个点的坐标, 下面就是对四个坐标进行各种数学运算
第一步: 生成向量函数
- function vector(a, b) {
- // 传入两个点, 生成这两个点构成的向量
- return {
- x: a.x - b.x,
- y: a.y - b.y
- }
- }
第二步: 获取向量积函数
- function vectorProduct(v1, v2) {
- // 传入两个向量, 获取向量积
- var mul = v1.x * v2.y - v2.x * v1.y;
- return mul;
- }
第三步: 传入一条向量和两个点对向量积进行是否同向判断, 在上文示例代码基础上改进
- function flag(v, O, P) {
- var flag = -1;
- var PA = vector(P, A);
- var OA = vector(O, A);
- var mul1 = vectorProduct(PA, v);
- var mul2 = vectorProduct(OA, v);
- if (mul1 * mul2 < 0) {
- // 两点不同向
- flag = -1;
- } else {
- // 两点同向
- flag = 1;
- }
- return flag
- }
第四部: 传入四个点, 调用公式, 判断是否需要延时
- function needDelay(A, B, O, P) {
- var V1 = vector(A, B);
- var V2 = vector(B, O);
- var V3 = vector(O, A);
- var x = flag(V1, O, P);
- var y = flag(V2, A, P);
- var z = flag(V3, B, P);
- if (x == 1 && y == 1 && z == 1) {
- return true;
- } else {
- return false;
- }
- }
再结合上篇导航优化方案预判用户行为 (一) 的代码, 就能完成最终效果
- if(needDelay){
- setTimeout(function(){
- // 导航切换代码
- },500);
- }else{
- // 导航切换代码
- }
效果图 GIF:
来源: https://juejin.im/post/5a7c2b1f5188257a5911b41d