在上篇京东大导航——预判用户行为(一)中讲到,我们对导航的切换操作设置了延时,解决了移入子菜单时不执行导航切换的问题。但同时引入了新的问题,即导航的延时造成了切换不流程。
如何才能做到,即保证用户进入子菜单的便捷,又能保证用户切换主导航的流程呢?
不防做这样的假设,如图
![](https://img.haomeiwen.com/i2865721/b7c651e8e40549bb.png)
其中图中的O点为鼠标的起始位置。
若鼠标移动路径位于阴影内部,就认为用户想要移动到当前子菜单,如路径O->p1
若鼠标移动路径位于阴影外部,就认为用户想要切换菜单,如路径O->p2
于是问题就转化成了,判断鼠标移动的方向是否位于阴影部分内部,如何判断一个点位于一个三角形的内部(P点位于三角形0AB内部),具体实现则用到了数学上的向量积问题。
数学思路:判断一个点是否位于一个三角形的内部
![](https://img.haomeiwen.com/i2865721/dcc962b46e3a2ed6.png)
同向法:沿着三角形三边走一圈A——>B——>C——>A,在AB边时,点P和C点在同侧;在BC边时,点P和A点在同侧;在CA边时,点P和B点在同侧;如果点P始终个第三个点保持同侧,则说明该点位于三角形内;若某一时刻两点位于两侧,那么该点在三角形外
那么问题又转化成了,判断两个点是否位于一条线段的同侧
这会儿就能使用向量积了。
![](https://img.haomeiwen.com/i2865721/3544062551495dfa.png)
如图,将向量PO和向量OA做叉乘,再将向量BO和OA做叉乘,如果两个叉乘的结果符号相同,说明两个点位于OA的同一侧
//向量积公式
OA × PO = (p.x – a.x)(p.y – a.y) – (p.y – a.y)(p.x – a.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://img.haomeiwen.com/i2865721/712088259c526464.gif)
大功告成!
刺不刺激,惊不惊喜
源码见个人github地址
网友评论