带有“鼠标方向”的悬停的 jQuery 动画

Posted

技术标签:

【中文标题】带有“鼠标方向”的悬停的 jQuery 动画【英文标题】:jQuery animation for a hover with 'mouse direction' 【发布时间】:2011-04-07 07:45:22 【问题描述】:

我正在尝试模拟将鼠标悬停在图像上的效果,叠加的半透明图像将从鼠标所在的方向淡入。反之亦然,当您的鼠标离开图像时(淡出 + 移开)

我为此准备了test page。去看看吧,它会弄清楚想要的效果是什么。

我为此定义了一个 html 结构:

    <div class="overlayLink">
        <img src="assets/work/thumbnails/kreatude.jpg"  />
        <div class="overlayLink_overlay_bg">&nbsp;</div>
        <div class="overlayLink_overlay_fg">
            <span class="overlayLink_overlay_link"><a href="#">View Case Study</a></span>
            <div class="top">&nbsp;</div>
            <div class="left">&nbsp;</div>
            <div class="right">&nbsp;</div>
            <div class="bottom">&nbsp;</div>
        </div>
     </div>

以及下面的 JS(我用的是 jQuery):

jQuery(document).ready(function () 
    ourWork();
);

function ourWork()
    var inHandler = function()
        var blue_bg = jQuery(this).find('.overlayLink_overlay_bg');
        var divClass = inClass;

        blue_bg.stop(true,true).fadeIn();
        var ml,mt;
        if(divClass == 'left')
            ml = -260;
            mt = 0;
        
        else if(divClass == 'right')
            ml = 260;
            mt = 0;
        
        else if(divClass == 'top')
            ml = 0;
            mt = -140;
        
        else if(divClass == 'bottom')
            ml = 0;
            mt = 140;
               

        //positioning
        jQuery(this).find('a').css(
            'marginLeft': ml + 'px',
            'marginTop' : mt + 'px'
        );


        //animation
        jQuery(this).find('a').stop(true,true).animate(
            "marginLeft": "0px",
            "marginTop": "0px"
        , "slow");
    
    var outHandler = function()
        var blue_bg = jQuery(this).find('.overlayLink_overlay_bg');
        var divClass = outClass;

        blue_bg.stop(true,true).fadeOut();
        var ml,mt;
        if(divClass == 'left')
            ml = -260;
            mt = 0;
        
        else if(divClass == 'right')
            ml = 260;
            mt = 0;
        
        else if(divClass == 'top')
            ml = 0;
            mt = -140;
        
        else if(divClass == 'bottom')
            ml = 0;
            mt = 140;
                

        //animation
        jQuery(this).find('a').stop(true,true).animate(
            "marginLeft": ml + "px",
            "marginTop": mt + "px"
        , "slow");

    

    var inClass, outClass;
    jQuery('.overlayLink_overlay_fg div').hover(function()        
        inClass = jQuery(this).attr('class');
    ,function()       
        outClass = jQuery(this).attr('class');
    );

    jQuery('.overlayLink').mouseenter(inHandler).mouseleave(outHandler);

解释:

基本上,当我将鼠标悬停在这 4 个 div 之一(.overlayLink_overlay_fg div)上时,我在图像顶部有四个绝对定位的 div 来知道方向(左、上、下、右)我把悬停的类名变量中的 div(var inClass 和 var outclass)

一旦我将鼠标悬停在覆盖图像区域的 div (.overlayLink) 上,我会从 inClass 或 outClass 变量请求方向并执行动画 (inHandler,outHandler)

然而这一切似乎都奏效了,当你快速移动鼠标时它有点小故障,现在我要问问题是什么(导致故障)以及如何解决它使用我当前的代码。

提前致谢

【问题讨论】:

您在上面链接到的页面有很多问题 - 这是您的问题吗?如果我从顶部下来,它会起作用。然后在左边它工作。然后,如果我再次从顶部向下走,它会起作用,但再次向左走会使覆盖层向上。然后,如果我从左侧进入,则覆盖层来自顶部。不管我做得多快。 你是对的,没有意识到它是如此的小故障。是的,这是我的页面。但是,发生这种情况一定有一些合乎逻辑的原因。如果我查看我的代码,我看不到其中的逻辑错误,这让我很困扰,哈哈。 别这样。这对触摸用户不公平。 【参考方案1】:

也许您应该考虑不要将 div 用作“热点”,而是使用一些数学和 javascript 来找到鼠标进入和离开 div 的点。这避免了重叠/间隙问题。下面的代码基本上将一个 div 划分为 4 个三角形区域。当鼠标移到每个区域上时,每个区域都会返回一个数字。代码需要一些微调,您必须自己决定在哪里绑定和取消绑定事件。但我认为它可以解决大部分闪烁问题。

$(".overlayLink").bind("mouseenter mouseleave",function(e)

/** the width and height of the current div **/
var w = $(this).width();
var h = $(this).height();

/** calculate the x and y to get an angle to the center of the div from that x and y. **/
/** gets the x value relative to the center of the DIV and "normalize" it **/
var x = (e.pageX - this.offset().left - (w/2)) * ( w > h ? (h/w) : 1 );
var y = (e.pageY - this.offset().top  - (h/2)) * ( h > w ? (w/h) : 1 );

/** the angle and the direction from where the mouse came in/went out clockwise (TRBL=0123);**/
/** first calculate the angle of the point, 
 add 180 deg to get rid of the negative values
 divide by 90 to get the quadrant
 add 3 and do a modulo by 4  to shift the quadrants to a proper clockwise TRBL (top/right/bottom/left) **/
var direction = Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180 ) / 90 ) + 3 )  % 4;


/** do your animations here **/ 
switch(direction) 
 case 0:
  /** animations from the TOP **/
 break;
 case 1:
  /** animations from the RIGHT **/
 break;
 case 2:
  /** animations from the BOTTOM **/
 break;
 case 3:
  /** animations from the LEFT **/
 break;
);

当然,获得方向的简写应该是:

var direction =  Math.round( Math.atan2(y, x) / 1.57079633 + 5 ) % 4

其中 1.57... 是 Math.PI / 2 这对我来说更难解释,因为它跳过了度数转换。

【讨论】:

另外,如果本机 javascript this.offsetTop 报告不正确,使用 $(this).offset().top 有效。我在 translateY 轴上移动身体,这与正确的偏移读数相混淆。仅供参考。 嗨 Caspar,我可以理解计算 x 和 y 以获得与 div 中心的角度的概念,但我不知道为什么需要( w &gt; h ? (h/w) : 1 )?你能帮忙理解一下吗?我想把它标准化(e.pageX - this.offsetLeft - (w/2))这就够了 @Sean,这是对 DIV 方向的修正;横向或纵向。【参考方案2】:

从根本上说,鼠标移动事件是一系列点,而不是我们在屏幕上感知的曲线。移动鼠标的速度越快,点之间的间距就越大,鼠标可以在不接触的情况下移动的对象就越大。因此,理想情况下,您需要知道最后一个鼠标位置的方向,然后才能落在您的对象上,并从中计算出它接近的方向。即,您需要不断地跟踪整个页面上的鼠标位置,以确保您的效果每次都能正常工作。

【讨论】:

【参考方案3】:

与其用绝对定位的 div 围绕图像,您可以尝试使用图像映射来执行此操作,将处理程序附加到定义的区域。只需将边界区域定义为足够厚,即使鼠标移动得相当快,也能检测到鼠标移动。

【讨论】:

这只是一个想法还是被认为是一种好的做法? 请注意您所说的“只是一个想法”是什么意思。用绝对定位的 div 包围你的图像是一个聪明的技巧。而图像映射和图像相互集成,提供更简洁的设计、更好的封装和更简单的页面布局。 另外,通过图像映射,您可以更轻松地调整不可见的“热”边界区域的粗细,更好地检测鼠标悬停,而不会影响布局。【参考方案4】:

如果您从左到右快速滑动鼠标,使其穿过 3 个矩形

     [ 1 ] [ 2 ] [ 3 ] 

在鼠标通过右边框 div 退出矩形 #2 后,鼠标将立即通过其左边框 div 进入矩形 #3。但是,当矩形 #3 通过其左边框 div 进入时,分配 inClass="left" outClass="left" 发生在矩形 #2 的 outHandler 触发之前。

【讨论】:

【参考方案5】:

如果我要实现这个功能,我会这样做:

    创建定义图像的顶部、底部、左侧和右侧边界的图像映射区域。 对于您需要此情报的每个图像(即,它从哪一侧进入,从哪一侧离开): a) 跟踪 sideEntered -- 这个值被赋值一次且仅一次;每当输入图像地图边界区域时,其鼠标悬停处理程序将咨询 sideEntered 并仅在尚未分配时分配它; b) 跟踪 lastSideTouched -- 每当进入图像映射区域时,它的 mouseoverhandler 都会重新分配此变量 c) 当鼠标离开矩形时,离开处理程序参考 lastSideTouched 并将 sideEntered 重置为 null

每个矩形都应该有自己的处理程序,因此 sideEnteredlastSideTouched 这两个重要值封装在其处理程序的范围内。

【讨论】:

请看我下面的解释,为什么快速移动会导致您的故障。【参考方案6】: 问题是什么(导致故障)

这段代码(以及 inClass 和 outClass 的使用)是有问题的。您正在更改事件处理程序中使用的全局变量的状态。

var inClass, outClass; 
jQuery('.overlayLink_overlay_fg div').hover(function()         
    inClass = jQuery(this).attr('class'); 
,function()        
    outClass = jQuery(this).attr('class'); 
); 

此代码假定 outClass 由“本地”div 设置,但可能已由相邻的顶部 div 设置

    var outHandler = function()        
    var blue_bg = jQuery(this).find('.overlayLink_overlay_bg');        
    var divClass = outClass;   
如何[可以]用我当前的代码修复它?

不要把它作为一个单一的全局变量来管理,试着用独立的方式来管理它。

这是一种方法: 我没有测试过这段代码

    jQuery('.overlayLink_overlay_fg div').hover(function() 
        jQuery(this).parents('.overlayLink').attr('mouseEnterClass', jQuery(this).attr('class'));
    , function() 
        jQuery(this).parents('.overlayLink').attr('mouseLeaveClass', jQuery(this).attr('class'));
    );

在您的处理程序中,引用这些新数据。

    var outHandler = function() 
        var blue_bg = jQuery(this).find('.overlayLink_overlay_bg');
        var divClass = jQuery(this).attr('mouseLeaveClass');
        ....
      

这将解决全局变量问题。我不确定如果在动画完成之前发生“悬停”事件会发生什么。

编辑: 修复了代码问题。

【讨论】:

【参考方案7】:

这是一个简单的解决方案,但边缘需要在 mouseenter 事件中进行改进。

在鼠标输入事件中,我在 (0% & 0%) (100% & 100%) 中设置了 5% 的差异,只是为了在光标快速移动时捕捉光标,这是需要修复的错误。

const div = document.querySelector('div');

const mouseLeave = (e) => 
    e.preventDefault();
    e = e || window.event;

    // get box properties
    let a = e.target.getBoundingClientRect();
    let w = a.width;
    let h = a.height;

    // get x,y
    const x = e.pageX - a.left;
    const y = e.pageY - a.top;

    //* detect mouse out
    if (x >= w) console.log('out-right');
    if (x <= 0) console.log('out-left');
    if (y >= h) console.log('out-bottom');
    if (y <= 0) console.log('out-top');


const mouseEnter = (e) => 
    e.preventDefault();
    e = e || window.event;

    // get box properties
    let a = e.target.getBoundingClientRect();
    let w = a.width;
    let h = a.height;

    // get x,y
    const x = e.pageX - a.left;
    const y = e.pageY - a.top;

    // get positions by percentage
    const X = (x / w) * 100;
    const Y = (y / h) * 100;

    //* detect mouse in
    if ((X >= 0 && X <= 100) && (Y >= 0 && Y <= 5)) console.log('in-top');
    if ((X >= 0 && X <= 100) && (Y >= 95 && Y <= 100)) console.log('in-bottom');
    if ((Y >= 0 && Y <= 100) && (X >= 95 && X <= 100)) console.log('in-right');
    if ((Y >= 0 && Y <= 100) && (X >= 0 && X <= 5)) console.log('in-left');


div.addEventListener('mouseleave', mouseLeave, true);
div.addEventListener('mouseenter', mouseEnter, true);
div 
    width: 300px;
    height: 300px;
    outline: 1px solid blue;
    margin: 50px auto;
&lt;div&gt;&lt;/div&gt;

【讨论】:

以上是关于带有“鼠标方向”的悬停的 jQuery 动画的主要内容,如果未能解决你的问题,请参考以下文章

jQuery在悬停时更改(带有淡入淡出动画)div的背景图像

悬停时的Jquery动画子菜单不流畅

有没有办法让这个 jquery 悬停动画更流畅?

悬停时的 jQuery 动画 |非常快的传球,停止动画

悬停(jQuery)上的动画过渡[关闭]

Jquery - 为啥我的图像不会在鼠标悬停时动画?