带有“鼠标方向”的悬停的 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"> </div>
<div class="overlayLink_overlay_fg">
<span class="overlayLink_overlay_link"><a href="#">View Case Study</a></span>
<div class="top"> </div>
<div class="left"> </div>
<div class="right"> </div>
<div class="bottom"> </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 > 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
每个矩形都应该有自己的处理程序,因此 sideEntered 和 lastSideTouched 这两个重要值封装在其处理程序的范围内。
【讨论】:
请看我下面的解释,为什么快速移动会导致您的故障。【参考方案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;
<div></div>
【讨论】:
以上是关于带有“鼠标方向”的悬停的 jQuery 动画的主要内容,如果未能解决你的问题,请参考以下文章