最近在zealer网站上看到一个有趣的动画效果,分析并实现了下,讲讲过程。
来,我们先看看效果(鼠标移动到按钮上):
See the Pen js-button-hover-follow-mouse by puronglong (@puronglong) on CodePen.
通过鼠标左右移动改变背景颜色,挺有意思的,这是怎实现的呢?
通过拆解这组动画会发现,其实原本是像下面这样的:
原来这个按钮的前面有一个一样大小的span,鼠标移动到按钮上的时候改变span的right属性,让它往右移动,在按钮的background和文字之间加了一个span,从而形成我们看到的效果。
了解了原理之后我们发现,重点在于获取到鼠标的位置,这个值与span的位置是息息相关的。
首先是骨架html:
<div class="wrap">
<div class="wrap_go">
<span style="right: 160px;"></span>
<a href="javascript:;">加载更多</a>
</div>
</div>
接着是我们的样式css:
.wrap_go{
width: 160px;
height: 40px;
position: relative;
margin: 1px auto 30px;
background: #111;
}
.wrap_go a{
color: #ffffff;
text-decoration: none;
width: 160px;
height: 40px;
line-height: 40px;
display: inline-block;
text-align: center;
z-index: 1;
position: relative;
}
.wrap_go span{
width: 160px;
height: 40px;
background: #414141;
position: absolute;
z-index: 0
}
可以看到现在的效果是这样的:
那么后面就是重要的一步,添加行为,我们使用jQuery实现:
首先,获取到wrap_go和span:
var wrap_go = $('.wrap').find('.wrap_go');
var span = wrap_go.find('span').eq(0);
在wrap_go上绑定事件,当我们mouseenter的时候,获取到当前的鼠标坐标,这里我们定义一个完成此功能的getMousePos函数,函数最终返回的值应该是鼠标在wrap_go里面的坐标,选取这个坐标的x轴值,用wrap_go的width长度的值减去获取的x轴值,就是span的right属性的值。
jquery里,pageX和pageY用来获取鼠标在整个页面中的坐标,用这个pageX减去wrap_go的偏移就是鼠标在wrap_go当中的位置了。
下面就是定义的getMousePos函数:
function getMousePos(e, n){
var left = 0, top = 0;
if (!e){
var event = window.event;
}
if (e.pageX || e.pageY) {
left = e.pageX;
top = e.pageY;
// 获得wrap_go元素当前的偏移,这是固定的
var a = n.offset();
// 当前鼠标在wrap_go的div里的坐标为鼠标在页面的坐标-wrap_go坐标
left -= a.left;
top -= a.top;
}
return{
left: left,
top: top
};
}
所以这个时候我们的js文件是这样的:
$(document).ready(function($) {
bindEvent();
function bindEvent() {
var i = this;
var wrap_go = $('.wrap').find('.wrap_go');
var span = wrap_go.find('span').eq(0);
wrap_go.on('mouseenter', function(i){
// mouseenter的时候获取enter时的坐标,这个$(this)就是wrap_go
var t = getMousePos(i, $(this));
console.log(t.left);
function getMousePos(e, n){
var left = 0, top = 0;
if (!e){
var event = window.event;
}
if (e.pageX || e.pageY) {
left = e.pageX;
top = e.pageY;
// 获得wrap_go元素当前的偏移,这是固定的
var a = n.offset();
// 当前鼠标在wrap_go的div里的坐标:鼠标坐标-wrap_go坐标
left -= a.left;
top -= a.top;
}
return{
left: left,
top: top
};
}
});
}
});
这样鼠标移入的时候就能看到效果了,但是还需要在鼠标mousemove的时候也改变它的样式,所以还需要添加一个mousemove事件:
把mousemove时获取的数据处理后赋值给span的css属性
wrap_go.on('mousemove', function(i) {
// mousemove的时候实时获取坐标
t = getMousePos(i, $(this));
span.css({
right: parseInt($(this).width() - t.left)
});
});
当mouseleave的时候恢复原来的样式:
wrap_go.on('mouseleave', function() {
// mouseleave的时候恢复样式
span.animate({
right: '160px'
});
});
从这里可以看到当鼠标mouseleave的时候,是有一个动画的,因此一进来mouseenter的时候,需要先阻止先前的动画,记得加上span.stop()。
以上,基本的效果就出来啦。
但是测试的时候发现一个问题,当鼠标多次移入移出的时候,该效果就不灵了,没有反应了。通过查看span的right属性分析可以看到,当我们enter的时候,是能获取到当前的一个坐标值的,但是很快这个right属性的值就又变为160px,也就是恢复原值了。照理这个恢复原值的操作应该在mouseleave的时候才发生啊,我这儿还没有leave呢。
知道是
span.animate({
right: '160px'
});
造成的问题以后,可以考虑再加一个span.stop()来阻止掉这个right改为160px的行为,加在哪里呢?加在mousemove的时候。
再来测试一遍,可以啦!
完整js如下:
$(document).ready(function($) {
bindEvent();
function bindEvent() {
var i = this;
var wrap_go = $('.wrap').find('.wrap_go');
var span = wrap_go.find('span').eq(0);
wrap_go.on('mouseenter', function(i){
span.stop();
// mouseenter的时候获取enter时的坐标
var t = getMousePos(i, $(this));
console.log(t.left);
wrap_go.on('mousemove', function(i) {
// mousemove的时候实时获取坐标
t = getMousePos(i, $(this));
span.css({
right: parseInt($(this).width() - t.left)
});
span.stop();
});
wrap_go.on('mouseleave', function() {
// mouseleave的时候恢复样式
span.animate({
right: '160px'
});
});
function getMousePos(e, n){
var left = 0, top = 0;
if (!e){
var event = window.event;
}
if (e.pageX || e.pageY) {
left = e.pageX;
top = e.pageY;
// 获得wrap_go元素当前的偏移,这是固定的
var a = n.offset();
// 当前鼠标在wrap_go的div里的坐标:鼠标坐标-wrap_go坐标
left -= a.left;
top -= a.top;
}
return{
left: left,
top: top
};
}
});
}
});