jQuery实现拉勾穿墙效果

A 预备知识

A1 思路

HTML结构是每一个展示框都附带了一个随着鼠标滑过的遮罩层(mask)。每当鼠标在展示框上移入移出的时候,判断鼠标的方向,让遮罩层来跟随鼠标的移动。
由于遮罩层只有一个,每次鼠标移入时,让遮罩层瞬间定位,然后沿着鼠标的方向进入,最终的目标就是和展示框的位置完全重合。
离开时正好相反,起点是和展示框完全重合的位置,终点就是遮罩层刚刚离开展示框。
行为如下图所示:

穿墙演示.png

A2 知识储备

  • 一丢丢平面几何的知识
  • js中盒子模型属性
  • jQuery的animate动画
  • 事件源的属性

B 代码堆积

B1 HTML结构

<ul id="list">
	<li>
		![](images/aiqiyi.jpg)
		<div class="mask">爱奇艺</div>
	</li>
        ...
</ul>

img是要穿过的图片
mask是遮罩层
li相对定位,mask绝对定位

B2 绑定事件

var $li = $('#list').children('li');
$li.each(function () {
	fn($(this));
});

遍历所有的li,在函数fn里为其绑定移入和移出事件

function fn($box) {
    var nWidth = $box.outerWidth();
    var nHeight = $box.outerHeight();
    var $mask = $box.children('.mask');
}

获得每个li的宽高,这个宽高也是遮罩层运动的距离。
获得遮罩层。

//在fn里,为每个li绑定移入事件
$box.mouseenter(function (e) {
    var x = e.clientX - $(this).offset().left - this.offsetWidth / 2;
    var y = $(this).offset().top + this.offsetHeight / 2 - e.clientY;
};

此入的x和y,是相对于各自的li的中心。

获得鼠标坐标值.png

鼠标的实时位置,相对于li的中心点的坐标,可以根据上图这样来算出

y = li高度/2 - (鼠标实时距页面边距b - li相对页面的偏移a);
x = 鼠标实时距页面边距d -  li相对页面的偏移c - li宽度/2;

备注:li距页面的边距不一定就是通过ele.offset.top来获得,如果中间嵌套了其他定位元素,可以由其他盒子模型属性来获得。

因为要根据这个xy值来判断,是哪个方向,而且离开的时候还是要根据这个来判断,所以将判断方法封装成一个方法,返回值有四种情况,来代码是从哪个方向进入和移出。

function getDirction(x, y) {
    //获得方向的弧度制
    var rad = Math.atan2(y, x);
    //获得方向的角度制,具体角度分布见下图
    var angle = 180 * rad / Math.PI;
    //将角度经过一系列运算,最终得到四个方向四个值,返回出去
    return Math.round((angle + 180) / 90) % 4;
}

得到方向.png

由于得到的5个值,左边进入有两值,再进行%4操作,就可以精简为四个值,分别0代表从左侧进入,1借债有从下侧进入,2代码从右侧进入,3代表从上侧进入。

在鼠标移入事件中得到方向,来控制mask的进入初始位置。

$box.mouseenter(function (e) {
    var x = e.clientX - $(this).offset().left - this.offsetWidth / 2;
    var y = $(this).offset().top + this.offsetHeight / 2 - e.clientY;
    var val = getDirction(x, y);
    switch (val) {
        case 0:
            $mask.css({'left': -nWidth, 'top': 0});
            break;
        case 1:
            $mask.css({'top': nHeight, 'left': 0});
            break;
        case 2:
            $mask.css({'top': 0, 'left': nWidth});
            break;
        case 3:
            $mask.css({'top': -nHeight, 'left': 0});
    }
    //无论从哪个方向进入,最终的目标点都是一样的,即和li完全重合,就是起始的位置不同
    $mask.stop().animate({'left': 0, 'top': 0});
});

同样由此可以得出,移出事件

$box.mouseleave(function (e) {
    var x = e.clientX - $(this).offset().left - this.offsetWidth / 2;
    var y = $(this).offset().top + this.offsetHeight / 2 - e.clientY;
    var val = getDirction(x, y);
    var target;
    switch (val) {
        case 0:
            target = {'left': -nWidth, 'top': 0};
	        break;
        case 1:
	        target = {'top': nHeight, 'left': 0};
	        break;
	    case 2:
	        target = {'top': 0, 'left': nWidth};
	        break;
	  case 3:
	        target = {'top': -nHeight, 'left': 0};
    }
    $mask.stop().animate(target);
});

移出事件正好和移入相反,移出的时候起始点是一样的,而离开的时候需要判断是从哪个方向出去。

C 在线预览

在线预览

原文地址:https://www.cnblogs.com/changzhenan/p/7071619.html