动画函数的封装
匀速动画
核心原理:通过定时器setInterval()不断移动盒子位置。
实现步骤:
1.获取盒子当前位置:offsetLeft
2.让盒子在当前位置加上1个移动距离: element.style.left
3.利用定时器不断重复这个操作:setInterval(fn, delay)
4.加上一个定时器的条件
5.注意此元素需要添加定位
.box{
position: absolute; // 必须添加定位
width: 100px;
height: 100px;
background-color:orange
}
<div class="box"></div>
对动画函数animate的封装,需要传入四个参数:
obj: 需要做动画的元素
target::移动的目标距离
delay: 多久调用一次定时器,值越小,移动的速度越快
callback:动画执行完毕之后,执行的回调函数
function animate(obj,target,delay,callback) {
// obj.timer 给不同的元素指定不同的定时器
obj.timer = setInterval(() => {
if(obj.offsetLeft > target) {
// 停止动画,本质就是停止定时器
clearInterval(obj.timer);
// 回调函数写在定时器停止之后
callback && callback();
}
// offsetLeft是只读属性,只能用来获取值,不能赋值,赋值要用element.style.left
obj.style.left = obj.offsetLeft + 10 + 'px'
}, delay);
}
var box = document.querySelector('.box');
// 调用函数
animate(box, 400, 30, function(){ // 运动到400时,弹出对话框
alert('已经移动到400')
})
以上是基本动画函数封装,但是假如现在有一个新需求,就是点击按钮之后,再让盒子做动画呢?
.box{
position: absolute;
width: 100px;
height: 100px;
background-color:orange
}
.btn{
position: absolute;
top:120px;
}
<div class="box"></div>
<button class="btn">按钮点击后运动</button>
function animate(obj,target,delay,callback) {
obj.timer = setInterval(() => {
if(obj.offsetLeft > target) {
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + 10 + 'px'
}, delay);
}
var box = document.querySelector('.box');
var btn = document.querySelector('.btn')
btn.addEventListener('click', function(){
animate(box, 400, 30)
})
但是这样写的话会有一个bug,就是连续点击按钮的时候,盒子的移动速度会慢慢变快,因为在内存中会不断创建新的定时器,定时器越多,物体移动就越快。因此,需要在调用定时器之前先清除掉内存中已有的定时器,使得只有一个定时器执行。
function animate(obj,target,delay,callback) {
clearInterval(obj.timer) // 先清除掉先前的定时器,只保留当前的一个定时器执行
obj.timer = setInterval(() => {
if(obj.offsetLeft > target) {
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + 10 + 'px'
}, delay);
}
var box = document.querySelector('.box');
var btn = document.querySelector('.btn')
btn.addEventListener('click', function(){
animate(box, 400, 30)
})
缓动动画
原理:让盒子的每次移动的距离慢慢变小,速度就会慢慢落下来。
计算公式: (目标值 - 现在的位置 ) / 10
注意: 这里的步长值step会出现小数的情况,要把它取整,否则会使移动的位置不准确。并且还要区分是正数还是负数,如果向左移动就是负数,向下取整(Math.floor),向右移动就是正数,向上取整(Math.ceil)。
.box{
position: absolute;
width: 100px;
height: 100px;
background-color:orange
}
.btn1{
position: absolute;
top:120px;
}
.btn2{
position: absolute;
top:120px;
left: 120px;
}
<div class="box"></div>
<button class="btn1">点击后运动800</button>
<button class="btn2">点击后运动500</button>
function animate(obj,target,delay,callback) {
clearInterval(obj.timer)
obj.timer = setInterval(() => {
var step = (target - obj.offsetLeft) / 10 // 计算步长,这步要写在定时器里面
step = step > 0 ? Math.ceil(step) : Math.floor(step) // 进行判断
if(obj.offsetLeft == target) {
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + step + 'px'
}, delay);
}
var box = document.querySelector('.box');
var btn1 = document.querySelector('.btn1')
var btn2 = document.querySelector('.btn2')
btn1.addEventListener('click', function(){
animate(box, 800, 30)
})
btn2.addEventListener('click', function(){
animate(box, 500, 30)
})
具体案例
我们已经将动画函数封装成函数,在需要用的地方进行调用animate。
需求:鼠标经过sliderbar,导航懒从右往左移滑动出来,箭头改变, 鼠标离开sliderbar,导航栏从左往右滑动回去,箭头改变。
.sliderbar {
position: fixed;
left: 0;
top: 100px;
width: 40px;
height: 40px;
text-align: center;
line-height: 40px;
cursor: pointer;
color: #fff;
}
.con {
position: absolute;
right: 0;
top: 0;
width: 200px;
height: 40px;
background-color: purple;
z-index: -1;
}
<div class="sliderbar">
<span>→</span>
<div class="con">问题反馈</div>
</div>
var sliderbar = document.querySelector('.sliderbar')
var con = document.querySelector('.con')
sliderbar.addEventListener('mouseenter', function(){
animate(con, 0, 15, function(){
// 当动画执行完毕,就把 → 改为 ←
sliderbar.children[0].innerHTML = '←'
})
})
sliderbar.addEventListener('mouseleave', function(){
animate(con, -160, 15, function(){
sliderbar.children[0].innerHTML = '→'
})
})