Web API(六)动画函数封装
一、动画函数封装
1、缓动动画原理
缓动动画就是让元素运动速度有所变化,最常见的是让速度慢慢停下来。
思路:
- 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。
- 核心算法: (目标值 - 现在的位置) / 10 做为每次移动的距离步长
- 停止的条件是: 让当前盒子位置等于目标位置就停止定时器
- 注意步长值需要取整
2、 动画函数多个目标值之间移动
注意:当动画函数从800移到500时,要判断步长是正值还是负值
1.如果是正值,则步长往大了取整
2.如果是负值,则步长 向小了取整
3、缓动函数添加回调函数
回调函数原理:函数可以作为一个参数。将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数,这个过程就叫做回调。
回调函数写的位置:定时器结束的位置。
4、封装缓动动画函数animate()★;
// 封装动画函数 animate(参数)
// 参数:obj 要做动画的元素对象;target 最终的目标位置
function animate(obj, target, callback) {
// 先销毁定时器
clearInterval(obj.timeID)
// 开启一个定时器 setInterval()
obj.timeID = setInterval(function() {
if (obj.offsetLeft == target) {
// 到达了目标位置,发出通知, 调用传过来的函数
// 当 callback 不为 undefinded 的时候,才去调用 (利用逻辑中断构建单条件)
// 回调函数写到定时器结束里面
// if (callback) {
// // 调用函数
// callback();
// }
callback && callback()
// 销毁定时器
clearInterval(obj.timeID)
return
}
// 缓动动画的步长 = (目标位置 - 当前位置) / 10
var step = (target - obj.offsetLeft) / 15
// step 取整操作: 如果 step 步长大于 0, 采取向上取整;如果 step 步长小于 0,采取向下取整
step = step > 0 ? Math.ceil(step) : Math.floor(step)
// 设置盒子的 left = 当前盒子的位置 + 每次移动的距离 + px
obj.style.left = obj.offsetLeft + step + 'px'
}, 15)
}
二、案例:轮播图
1、js思路:
-
获取元素
-
鼠标经过焦点图,显示左右按钮,鼠标离开隐藏它
-
创建小圆圈,个数要和图片张数保持一致
-
点击小圆圈,让当前点击的小圆圈高亮,其他保持不变
-
点击小圆圈后,移动焦点图到指定的位置
-
排他思想设置小圆圈的过程抽取出来作为一个单独的函数
-
点击右侧按钮,播放下一张图片
-
实现一个无缝滚动(通过在 ul 最后添加一张图片)
-
播放对应的小圆圈
-
解决小圆圈的播放和按钮的播放不同步的问题
-
点击左侧按钮,播放上一张图片
-
自动播放轮播图(1.5s)
-
解决点击按钮播放速度过快的问题(上一张还没有播放完,下一张已经开始) 节流阀
document.addEventListener('DOMContentLoaded', function() {
console.log(123);
// 获取元素
var focus = document.querySelector('.focus');
var left = document.querySelector('.arrow-l');
var right = document.querySelector('.arrow-r');
var ul = document.querySelector('.slider');
var ol = document.querySelector('.circle');
// 获取focus的宽度, 也就是一张图片的宽度
var w = focus.offsetWidth;
var num = 0; //计数
var flag = true; //增添节流阀
// console.log(w);
// console.log(focus, left, right, ul, ol);
// 鼠标经过,左右侧按钮显示
focus.addEventListener('mouseover', function() {
left.style.display = 'block';
right.style.display = 'block';
// 鼠标进过,轮播图停止
clearInterval(timeID);
});
// 鼠标经过,左右侧按钮隐藏
focus.addEventListener('mouseout', function() {
left.style.display = 'none';
right.style.display = 'none';
// 鼠标离开,轮播图开始
clearInterval(timeID); //先清除之前
timeID = setInterval(function() {
right.click();
}, 1000)
});
// 优化操作,将点击的小圆圈高亮抽取出来
function setCircle(index) {
// 利用排他思想将点击的小圆圈高亮
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
ol.children[index].className = 'current';
}
// 为ol添加li
for (var i = 0; i < ul.children.length; i++) {
var li = document.createElement('li');
ol.appendChild(li);
// ol.innerHTML += "<li></li>";
// 为ol中每个小li添加自定义属性index, 方便ul中图片的转换
li.setAttribute('index', i);
// 为li标签添加点击事件
li.addEventListener('click', function() {
// 获取ol中每个li对应的index值
var index = this.getAttribute('index');
setCircle(index);
// 点击ol中li后,调用缓动动画函数,根据每个li对应的index值,ul移动相应的距离,显示对应的图片,注意向左移动应该是负值
animate(ul, -index * w);
// 解决小圆圈的播放和按钮的播放不同步的问题
// 问题:先点击小圆圈再点击右侧按钮,图片播放的顺序会乱掉
// 解决:每次去点击小圆圈时,把index值赋值给num,进行同步
num = index;
})
}
ol.children[0].className = 'current';
// 为右侧点击按钮添加点击事件
right.addEventListener('click', function() {
// console.log(flag);
// 每次点击之后,先判断当前阀门是否打开
if (flag == false) {
return; //当阀门关闭,终止函数
}
flag = false;
// 判断当前是否播放到了最后这一张
if (num == ol.children.length) {
console.log('播放到最后一张');
// 瞬间把ul拉到第一张图片位置
ul.style.left = 0;
// 将num重置为0
num = 0;
}
// 播放下一张
num++;
// 调用缓动函数
animate(ul, -num * w, function() {
// 当动画结束后,将阀门打开
flag = true;
});
// 播放小圆圈, num % ol.children.length,当到达最后这一张时,求余为0,调到第一张
setCircle(num % ol.children.length);
// num == 3 ? setCircle(0) : setCircle(num)
})
// 点击左侧按钮,播放上一张图片
left.addEventListener('click', function() {
// 每次点击之后,先判断当前阀门是否打开
if (flag == false) {
return; //当阀门关闭,终止函数
}
flag = false;
// 判断当前是否播放到了第一张
if (num == 0) {
console.log('播放到第一张');
// 将num重置为最后一张图片的索引
num = ol.children.length;
// 瞬间把ul拉到最一张图片位置
ul.style.left = -num * w + 'px';
}
// 播放上一张
num--;
// 调用缓动函数
animate(ul, -num * w, function() {
// 当动画结束后,将阀门打开
flag = true;
});
// 播放小圆圈, num % ol.children.length,当到达最后这一张时,求余为0,调到第一张
setCircle(num % ol.children.length);
// num == 3 ? setCircle(0) : setCircle(num)
})
// 克隆ul第一个li到末尾,目的:当ul移动到最后一张时,同时把ul位置重置到原始位置,使播放更加流畅
var firstli = ul.children[0].cloneNode(true);
ul.appendChild(firstli);
// 自动播放轮播图,开启定时器(1.5s)
var timeID = setInterval(function() {
right.click(); //自动触发右击事件
}, 1000)
})
2、节流阀
防止轮播图按钮连续点击造成播放过快。
节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。
核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。
开始设置一个变量var flag= true;
If(flag){flag = false; do something} 关闭水龙头
利用回调函数动画执行完毕, flag = true 打开水龙头
三、触屏事件
1、触屏事件概述
移动端兼容性相对较好,H5、C3都可以放心使用,不用考虑 IE 浏览器兼容性。
移动端事件是通过手指触发的,触屏事件 touch
touchstart 手指触摸到元素时触发 (mousedown)
touchmove 手指在元素上移动时触发 (mousemove)
touchend 手指离开元素时触发 (mouseup)
2、触摸事件对象
- touchevent 事件对象主要属性
- targetTouches: 触摸当前元素的手指集合 (主要记住)
- touches:触摸屏幕的所有手指集合
- changedTouches: 当前元素上的触摸状态(从有到无,从无到有)发生变化时触发
-
获取当前触摸手指的页面坐标
横坐标:e.targetTouches[0].pageX
纵坐标:e.targetTouches[0].pageY
-
手指移动元素时防止整个页面进行滚动
阻止事件默认行为, 添加 e.preventDefault()
3、案例:移动端的拖拽事件
var box = document.querySelector('.box');
var x, y;
box.addEventListener('touchstart', function(e) {
x = e.targetTouches[0].pageX - this.offsetLeft;
y = e.targetTouches[0].pageY - this.offsetTop;
})
box.addEventListener('touchmove', function(e) {
var left = e.targetTouches[0].pageX - x;
var top = e.targetTouches[0].pageY - y;
box.style.left = left + 'px';
box.style.top = top + 'px';
e.preventDefault();
})
拓展:
屏幕可用工作区高度:window.screen.availHeight
屏幕可用工作区宽度:window.screen.availWidth