动画
- 运动
-
- 匀速运动
- 加速匀速
- 减速运动
- 缓冲运动(在某个区间内做减速运动)
- 封装缓冲运动的函数
- 需求:当定时器全部清除完时(这里有多个定时器),让元素背景改为red
- 问题
- 如何让元素透明也可以运动变化
- 圆周运动
- 封装动画函数(可调用动画函数)
- 轮播图效果(第一种方法)
-
- animation.js文件
- html代码
- 轮播图效果(第二种方法)
- 导航栏筋斗云效果
运动 动画必须和定时器一起实现
匀速运动
Document - 锐客网
>
.box {
width: 100px;
height: 100px;
background: skyblue;
position: absolute;
left: 0;
}
>
// 获取元素
let box = document.querySelector('.box')
box.onclick = function () {
let left = 0
let timer = setInterval(function () {
// 每次定时器移动同样的距离
left += 13
if (left >= 800) {
// 固定移动的距离
left = 800
// 判断条件成立清除定时器
clearInterval(timer)
}
box.style.left = left + 'px'
}, 50)
}
加速匀速
Document - 锐客网
>
.box {
width: 100px;
height: 100px;
background: skyblue;
position: absolute;
left: 0;
}
>
// 获取元素
let box = document.querySelector('.box')
box.onclick = function () {
let left = 0
let speed = 0
let timer = setInterval(function () {
// 开启每次定时器时,只需要移动比上一次更多的距离
// 第一次为0 left为0
// 第二次为10 left 为10
// 第三次为20 left 为10 + 20
// 第四次为30 left 为30 + 30
speed += 10
left = left + speedif (left >= 800) {
left = 800
clearInterval(timer)
}
box.style.left = left + 'px'
}, 50)
}
减速运动
Document - 锐客网
>
.box {
width: 100px;
height: 100px;
background: skyblue;
position: absolute;
left: 0;
}
>
// 获取元素
let box = document.querySelector('.box')
box.onclick = function () {
let left = 0
let speed = 100
let timer = setInterval(function () {
// 开启每次定时器时,只需要移动比上一次更少的距离
// 第一次为100 left 为 100移动 100
// 第二次为90left 为 190移动 90
// 第三次为80left 为 270移动 80
// 第四次为70left 为 340移动 70
speed -= 10
left = left + speedif (speed <= 0) {
clearInterval(timer)
//console.log(1)
}
box.style.left = left + 'px'
}, 50)
}
缓冲运动(在某个区间内做减速运动)
Document - 锐客网
>
.box {
width: 100px;
height: 100px;
background: skyblue;
position: absolute;
left: 0;
}
>
/*
缓冲运动:在某个区间内做减速运动
如果是之前的动画减速运动的话不能固定区间距离
假如在0-800内做减速运动
speed =(目标值 - 当前值) / 系数
此处的目标值为设定的值,当前值为移动后的值,系数随意即可
*/
// 获取元素
let box = document.querySelector('.box')
box.onclick = function () {
let left = 0
let speed = 0let target = 800
let timer = setInterval(function () {
// target-left值会越来越小,因为下一次定时器的left是变化的
/*
第一次执行定时器:
target 为 800
left为 0
speed为 80
第一次执行定时器:
target 为 800
left为 0+8080
speed为 (800-80)10 72
*/
speed = Math.ceil((target - left) / 5)
left = left + speed
// 如果 speed = (target - left) / 5
// 下面的if判断并没有执行,因为存在小数点,会一直不成立,
// 如果 speed = parseInt((target - left) / 5) 还是不成立,
// 因为如果为 799.1 取整还是799 ,如果为799.9, 取整还是799,所以一直不等于800
// 如果 Math.ceil((target - left) / 5)向上取整
// 因为如果为 799.1 向上取整是800 ,如果为799.9, 向上取整是800,所以判断成立
// 成立就可以执行if判断语句,并关闭定时器
if (left === target) {
clearInterval(timer)
}
box.style.left = left + 'px'
// console.log(speed, left)
// console.log(1)
}, 50)
}
封装缓冲运动的函数
Document - 锐客网
>
.box {
width: 100px;
height: 100px;
background: skyblue;
position: absolute;
left: 0;
}#btn {
position: absolute;
top: 120px;
}
>// 获取元素
let box = document.querySelector('.box')
let btn = document.querySelector('#btn')// 封装一个函数 来获取非行内样式
function getStyle(ele, attr) {
var style;
if (ele.currentStyle) {
//ele.currentStyle 包含 所有css样式的对象
// obj.attr 获取 obj的 attr的属性
style = ele.currentStyle[attr];
} else {
//正常浏览器具有getComputedStyle()方法
style = window.getComputedStyle(ele)[attr];
}
// 把获取的样式返回
return style
}// 封装一个缓冲运动 动画函数
// ele是元素, attr是样式, target是目标值
function animation(ele, attr, target) {
let speed;
let timer = setInterval(() => {
// getStyle(ele, attr) 返回值是带有单位的字符串
let style = parseInt(getStyle(ele, attr))
speed = Math.ceil((target - style) / 5)
style = style + speed
if (style === target) {
// console.log(1) 判断是否关闭定时器
clearInterval(timer)
}
// 这里的attr是字符串,需要用[]
ele.style[attr] = style + 'px'
// console.log(2) 判断是否开启定时器
}, 50)
}btn.onclick = function () {
animation(box, 'width', 500)
}
需求:当定时器全部清除完时(这里有多个定时器),让元素背景改为red
Document - 锐客网
>
.box {
width: 100px;
height: 100px;
background: skyblue;
position: absolute;
left: 0;
}#btn {
position: absolute;
top: 120px;
}
="../js/utils.js">
>
// 需求:当定时器全部清除完时,让元素背景改为red// 获取元素
let box = document.querySelector('.box')
let btn = document.querySelector('#btn')// 封装一个动画函数
function animation(ele, obj) {
// 定义一个变量timerLen
let timerLen = 0
let speed;
// 对象循环 key为对象里面的属性 left widht height
for (let key in obj) {
// 当每次遍历时,让timerLen加1
timerLen++
let timer = setInterval(() => {
let style = parseInt(getStyle(ele, key))
speed = Math.ceil((obj[key] - style) / 10)
style = style + speed
if (style === obj[key]) {
clearInterval(timer)
// 每清除一次定时器让timerLen减1
timerLen--
// 然后判断定时器是否清除完 为0 说明动画结束
if (timerLen === 0) {
ele.style.background = 'red'
}
}
ele.style[key] = style + 'px'
}, 500)
}
}btn.onclick = function () {
// animation(box, left, 800)
// 当有多个样式需要动画效果时,可以定义一个对象数据
animation(box, { left: 800, width: 400, height: 400 })}
文章图片
文章图片
问题 但是会发现上面的那个封装动画有问题,当重复点击时,元素box却移动很快
这里了解的定时器的会知道,重复点击会重新调用函数,并且重新生成多个定时器,所以导致移动同样的距离而时间变短了,就会加快元素移动,那怎么解决呢???
动画 - 锐客网
>
.box {
width: 100px;
height: 100px;
background: skyblue;
position: absolute;
left: 0;
}#btn {
position: absolute;
top: 120px;
}
="../js/utils.js">
>
// 需求:当定时器全部清除完时,让元素背景改为red// 获取元素
let box = document.querySelector('.box')
let btn = document.querySelector('#btn')// 封装一个动画函数
function animation(ele, obj, callback) {
let timerLen = 0
let speed;
for (let key in obj) {
timerLen++
// 清除定时器
/*
let timer = 定时器
不能定义变量来接收定时器
如果使用变量的时候,点击的时候会创建一个函数地址
变量在函数中是属于局部作用域,
下一次点击的时候不能获取上一次点击时候创建的变量
clearInterval(timer)清除的是当前timer的值,当前的timer = undefined把定时器 给到元素的属性中
元素是一个dom 也是一个对象,以地址的形式
当第一个点击的时候 给dom对象对象添加了属性,
下一次点击的时候是能获取 上一次给dom添加的属性
*/
clearInterval(ele[key])
// 给这个对象添加一个 left 属性,属性值为 一个定时器的空间地址
ele[key] = setInterval(() => {
let style = parseInt(getStyle(ele, key))
speed = Math.ceil((obj[key] - style) / 10)
style = style + speed
if (style === obj[key]) {
clearInterval(ele[key])
// 每清除一次定时器让timerLen减1
timerLen--
// 然后判断定时器是否清除完 为0 说明动画结束
if (timerLen === 0) {
// ele.style.background = 'red'
callback && callback()
}
}
ele.style[key] = style + 'px'
}, 500)
}
}btn.onclick = function () {
// animation(box, left, 800)
// 当有多个样式需要动画效果时,可以定义一个对象数据
animation(box, { left: 800, width: 400, height: 400 }, function () {
box.style.background = 'red'
})
}
注意:上面增加callback回调函数来操作元素,这样就不用在封装函数里面操作元素
let timer = 定时器
不能定义变量来接收定时器
如果使用变量的时候,点击的时候会创建一个函数地址
变量在函数中是属于局部作用域,
下一次点击的时候不能获取上一次点击时候创建的变量
clearInterval(timer) 清除的是当前timer的值,当前的timer = undefined
把定时器 给到元素的属性中【案例|动画之匀速加速减速缓冲运动(加轮播图)】以下问题在敲代遇见的。
元素是一个dom 也是一个对象,以地址的形式
当第一个点击的时候 给dom对象对象添加了属性,
下一次点击的时候是能获取 上一次给dom添加的属性
文章图片
文章图片
如何让元素透明也可以运动变化
Document - 锐客网
>
.box {
width: 100px;
height: 100px;
background: pink;
position: absolute;
left: 0px;
top: 0px;
opacity: 0.1;
}
="js/utils.js">
>
let box = document.querySelector('.box');
// 这里的opacity为0-100以内
move(box, { opacity: 55 }, function () {
alert('动画执行完成')
})/*
运动函数: 有3个参数
参数1:dom元素 参加动画元素
参数2:一个对象,这个元素什么属性参加动画
参数3:一个回调函数
*/
function move(ele, obj, callback) {let timerLen = 0;
for (let key in obj) {
timerLen++
let speed;
clearInterval(ele[key])
// 给这个对象添加一个 left 属性,属性值为 一个定时器的空间地址
ele[key] = setInterval(() => {
let style;
// 0-1 *100 == 0-1000.1*100 = 10
if (key === 'opacity') {
style = getStyle(ele, key) * 100;
} else {
style = parseInt(getStyle(ele, key));
}
// 0 - 4 = -4/5 = -0.8 向上取整 = 0
speed = (obj[key] - style) / 5;
// 如果计算出来的 speed 大于0 向上取整,如果小于0就向下取整
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
style = style + speed;
if (style === obj[key]) {
clearInterval(ele[key]);
timerLen--;
if (timerLen === 0) {
// ele.style.background = 'blue';
// console.log(1);
// 短路运算 如果有callback 就执行callback,没有就不执行
callback && callback();
}
}
if (key === 'opacity') {
ele.style[key] = style / 100;
} else {
ele.style[key] = style + 'px';
}
}, 60)
}}
圆周运动
- 圆周运动
- Math.cos(弧度)
- 余弦(cos):邻边比斜边 cosA = b / 半径 b = 半径 * cos(弧度)
- Math.sin(弧度)
- 正弦(sin):对边比斜边 sinA = a / 半径 a = 半径 * sin(弧度)
- 弧长 = (角度 * Math.PI) / 180°
Document - 锐客网
>
.box {
width: 300px;
height: 300px;
border: 2px solid red;
border-radius: 50%;
position: absolute;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px;
margin: auto;
}.box1 {
width: 30px;
height: 30px;
background: pink;
border-radius: 50%;
position: absolute;
}
>
let box = document.querySelector('.box');
let box1 = document.querySelector('.box1');
// 求大盒子的半径
let boxR = box.offsetWidth / 2;
// 求小盒子的半径
let box1R = box1.offsetWidth / 2;
let deg = 0;
render()
setInterval(() => {
deg += 10;
render()
}, 50)function render() {
// 给box1定位
// 弧度 = (角度 * Math.PI) / 180
let hudu = (deg * Math.PI) / 180;
// 求邻边和对边
// a为对边
let a = Math.sin(hudu) * boxR;
// b为领边
let b = Math.cos(hudu) * boxR;
box1.style.left = b + boxR - box1R + 'px';
box1.style.top = a + boxR - box1R + 'px';
}
封装动画函数(可调用动画函数)
/*
运动函数: 有3个参数
参数1:dom元素 参加动画元素
参数2:一个对象,这个元素什么属性参加动画
参数3:一个回调函数
注意:opacity的值 为0-100以内
getStyle()调用获取元素的样式函数
*/
function move(ele, obj, callback) {let timerLen = 0;
//当里面有多个属性时,用对象遍历的方法
for (let key in obj) {
timerLen++
let speed;
clearInterval(ele[key])
// 给这个对象添加一个 left 属性,属性值为 一个定时器的空间地址
ele[key] = setInterval(() => {
let style;
// 0-1 *100 == 0-1000.1*100 = 10
if (key === 'opacity') {
style = getStyle(ele, key) * 100;
} else {
style = parseInt(getStyle(ele, key));
}
// 0 - 4 = -4/5 = -0.8 向上取整 = 0
speed = (obj[key] - style) / 5;
// 如果计算出来的 speed 大于0 向上取整,如果小于0就向下取整
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
style = style + speed;
if (key === 'opacity') {
ele.style[key] = style / 100;
} else {
ele.style[key] = style + 'px';
}// 如果先执行回调函数 再给 元素设置 样式,会先把回调函数中的
// 所有 代码执行完成之后再执行后面代码
if (style === obj[key]) {
clearInterval(ele[key]);
timerLen--;
if (timerLen === 0) {
// 短路运算 如果有callback 就执行callback,没有就不执行
callback && callback();
}
}// 轮播图的时候注意:当时间 为60的时候,30次 1800毫秒 才能把内层的定时器执行完成
// 外层的定时器 是1300毫秒执行一次,会造成的问题就是 内层的定时器还没有执行完成 外层定时器有重新执行了}, 30)
}
}// 封装一个函数 来获取非行内样式
function getStyle(ele, attr) {
var style;
if (ele.currentStyle) {
//ele.currentStyle 包含 所有css样式的对象
// obj.attr 获取 obj的 attr的属性
style = ele.currentStyle[attr];
} else {
//正常浏览器具有getComputedStyle()方法
style = window.getComputedStyle(ele)[attr];
}
// 把获取的样式返回
return style
}
轮播图效果(第一种方法) animation.js文件
/*引用狗哥封装函数注释和解析*///动画:animation(ele, obj, callback)
//参数1:为需要运动的元素 ; 参数2:为元素的属性的修改,以对象的形式(注意点:透明度设置为0.5则需要0.5 * 100)
//参数3:为回调函数,就是待动画执行完毕后,需要操作
function animation(ele, obj, callback) {let timerLen = 0;
//定时器的执行次数
//遍历对象obj 进行动画样式的设置!
for (let key in obj) {
timerLen++
// console.log(key);
//字符串的格式 key --属性 top等等
// console.log(obj[key]);
//也就是目标值 属性值obj[key] 100
//style就是所运动的样式
clearInterval(ele[key]) //清除上一次的定时器let speed = 0;
//速度
ele[key] = setInterval(() => {//假如为透明属性的时候,就获取到初始的透明值,否则就是获取当前样式的属性!
if (key === "opacity") {
//属性值的初始设置为小于1的,为透明度的时候 值需要*100,为了方便计算!
style = getStyle(ele, key) * 100 //拿到初始的属性值
} else {
var style = parseInt(getStyle(ele, key));
//获取到非行内样式
}
//速度的计算! (目标值 - 当前样式值)/5
speed = (obj[key] - style) / 5;
//假如速度小于0的时候,就是代表负数,向下取整(负数)
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
style = style + speed //所修改的样式 = 当前样式 + 速度// console.log(style, obj[key]);
//50 100
if (key === "opacity") { //由于透明属性没有那个单位,因此需要判断!
ele.style[key] = style / 100 //修改ele元素的样式
} else {
ele.style[key] = style + "px" //修改ele元素的样式
}if (style === obj[key]) { //当left等于目标值的之后,就清除定时器!
clearInterval(ele[key])
timerLen-- //执行完定时器后,就定时器计数渐渐 当定时器计数器为0的时候,代表动画执行结束
if (timerLen === 0) {
// console.log("动画执行完毕了");
callback()
}
}
}, 30)
}
}//封装一个获取非行内样式的函数--- getStyle(box, 'height')
//参数1:为需要获取的元素,参数2:为获取元素的属性(需要添加'')返回值:为style
function getStyle(ele, attr) {
var style;
if (ele.currentStyle) {
// ele.currentStyle 包含 所有css样式的对象 需要使用obj['属性']
style = ele.currentStyle[attr]
} else {
style = window.getComputedStyle(ele)[attr]
}
return style
}
html代码
Document - 锐客网
>
* {
padding: 0;
margin: 0;
}ul,
li {
list-style: none;
}.box {
width: 600px;
height: 300px;
border: 2px solid #ccc;
position: relative;
margin: auto;
margin-top: 100px;
overflow: hidden;
}.box .imgBox {
width: 500%;
height: 300px;
display: flex;
position: absolute;
left: -600px;
}.box .imgBox li {
width: 600px;
height: 300px;
display: flex;
justify-content: center;
align-items: center;
font-size: 50px;
font-weight: 900;
color: #fff;
flex-shrink: 0;
}.box .pointBox {
height: 20px;
position: absolute;
bottom: 30px;
right: 20px;
display: flex;
justify-content: space-evenly;
align-items: center;
border-radius: 10px;
}.box .pointBox li {
width: 25px;
height: 25px;
background: rgba(0, 0, 0, .4);
border-radius: 50%;
cursor: pointer;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
margin-right: 5px;
}.box .pointBox li.active {
background: rgba(255, 255, 0, .5);
}.box .leftRightTabs {
position: absolute;
top: 50%;
width: 100%;
transform: translateY(-50%);
display: flex;
justify-content: space-between;
}.box .leftRightTabs span {
width: 40px;
height: 40px;
color: #fff;
font-size: 30px;
cursor: pointer;
user-select: none;
background: rgba(0, 0, 0, .4);
display: flex;
justify-content: center;
align-items: center;
}.box .leftRightTabs span:nth-child(1) {
border-radius: 0 50% 50% 0;
}.box .leftRightTabs span:nth-child(2) {
border-radius: 50% 0 0 50%;
}
="./js/animation.js">
- 1
- 2
- 3
- 4
- 5
class="left"><
class="right">>
>
/*
1. 获取元素
2. 设置焦点,根据 imgBox 里面 li 的个数设置焦点
3. 复制元素,第一个元素复制出来放在末尾,最后一只复制出来放在头
4. 自动轮播,搞一个定时器,每隔多少时间 index++ 动一下
5. 移入移出,移入的时候停掉 autoLoop
6. 点击左右按钮切换
7. 点击焦点切换
+ 要拿到点击的哪个焦点的 索引
+ 可不可以在setPoint 的时候保存一个索引在 li 身上
+ 点每一个 li 的时候拿到身上的属性就可以了
*/window.onload = function () {
// 获取imgBox元素
let imgBox = document.querySelector('.imgBox');
// 获取imgBox下的所有子元素
let list = imgBox.children;
// 获取小圆点元素的父元素
let pointBox = document.querySelector('.pointBox');
// 获取整个div 元素
let box = document.querySelector('.box');
// 获取盒子的宽度 此处为600
let boxWidth = box.clientWidth;
// 获取左右切换元素的父元素
let leftRightTabs = document.querySelector('.leftRightTabs');
// 设置一个变量为true 用于左右切换
let loopFlag = true;
// 渲染数据 设置焦点的(小圆点)
setPoint(list, pointBox)// 克隆第一张和最后一个元素(需要在渲染完小圆点数据之后操作)
let copyFirst = imgBox.firstElementChild.cloneNode(true);
let copyLast = imgBox.lastElementChild.cloneNode(true);
imgBox.appendChild(copyFirst);
//把赋值出来的第一张添加在imgBox的最后面
imgBox.insertBefore(copyLast, imgBox.firstElementChild);
//把赋值出来的最后一张添加在imgBox的第一张前面imgBox.style.width = boxWidth * imgBox.children.length + 'px';
//把装轮播图的容器的宽度变大// 轮播图运动起来
let index = 1;
// 定义一个定时器变量
let timer;
// 自动播放
autoPlay()// 鼠标滑过时候清除定时器,停止播放
box.addEventListener('mouseover', () => clearInterval(timer))
// 鼠标移出的时候重新调用函数,再重新播放
box.addEventListener('mouseout', () => autoPlay())// 封装一个设置小圆点的函数
function setPoint(arr, pointBox) {
// 排他思想,清除所有的小圆点样式
for (let i = 0;
i < arr.length;
i++) {
let li = document.createElement('li');
// 当是第一个点的时候,给这个点设置class名为 active
if (i === 0) {
li.classList.add('active')
}
// 显示小圆点里面的内容
li.innerHTML = i + 1;
// 自定义属性 属性值为索引值
li.setAttribute('point-index', i)
// 添加到 父元素 pointBox 里面
pointBox.appendChild(li);
}
}// 自动播放函数
function autoPlay() {
timer = setInterval(() => {
index++;
// 调用动画函数 imgbox 元素,向右运动,回调函数 moveEnd
animation(imgBox, { left: -index * 600 }, moveEnd);
}, 2000)
}// 运动结束之后操作的事情 moveEnd函数
function moveEnd() {
// 当index为轮播图循环完之后的长度减1时,当index为6,重新从第二张开始
// console.log(imgBox.children.length) // 7
// console.log(index) // 从2开始打印
if (index === imgBox.children.length - 1) {
index = 1;
// 向右移动的距离
imgBox.style.left = -index * boxWidth + 'px';
}
// 多判断一下,当index === 0
if (index === 0) {
index = imgBox.children.length - 2;
imgBox.style.left = -index * boxWidth + 'px';
};
// 判断小圆点的样式,清除所有的样式
for (let i = 0;
i < pointBox.children.length;
i++) {
pointBox.children[i].classList.remove('active');
};
// 给当前的小圆点添加样式
pointBox.children[index - 1].classList.add('active');
// 让loopflag改为true
loopFlag = true;
}// 点击左右按钮的时候,切换轮播图
// 委托给左右切换的父元素绑定事件
leftRightTabs.addEventListener('click', e => {
// 判断点击事件,如果为false,结束点击事件,防止开启多个动画,也就是开启多个定时器
if (loopFlag === false) {
return
};
// 如果不是false,先改变这个值为false
loopFlag = false;
if (e.target.className === 'left') {
index--;
// console.log(index) //打印 1 0 4 3 2
animation(imgBox, { left: -index * 600 }, moveEnd);
} else if (e.target.className === 'right') {
index++;
// console.log(index) //打印 6 2 3 4 5
animation(imgBox, { left: -index * 600 }, moveEnd);
}
})// 点击小圆点的时候也可以切换图片
// 委托给小圆点的父元素绑定事件
pointBox.addEventListener('click', e => {
// 判断点击事件,如果为false,结束点击事件,防止开启多个动画,也就是开启多个定时器
if (loopFlag === false) {
return
};
// 如果不是false,先改变这个值为false
loopFlag = false;
// 判断选中是否为li
if (e.target.tagName === 'LI') {
// console.log(e.target.getAttribute('point-index')) //打印的是字符串 需要转为数值型 - 0
index = e.target.getAttribute('point-index') - 0 + 1
// console.log(index) //打印 1 2 3 4 5
animation(imgBox, { left: -index * boxWidth }, moveEnd)
}
})
}
轮播图效果(第二种方法) 轮播图index.js
window.addEventListener('load', function () {
// 1. 获取元素
var arrow_l = document.querySelector('.arrow-l');
var arrow_r = document.querySelector('.arrow-r');
var focus = document.querySelector('.focus');
var focusWidth = focus.offsetWidth;
// 2. 鼠标经过focus 就显示隐藏左右按钮
focus.addEventListener('mouseenter', function () {
arrow_l.style.display = 'block';
arrow_r.style.display = 'block';
clearInterval(timer);
timer = null;
// 清除定时器变量
});
focus.addEventListener('mouseleave', function () {
arrow_l.style.display = 'none';
arrow_r.style.display = 'none';
timer = setInterval(function () {
//手动调用点击事件
arrow_r.click();
}, 2000);
});
// 3. 动态生成小圆圈有几张图片,我就生成几个小圆圈
var ul = focus.querySelector('ul');
var ol = focus.querySelector('.circle');
// console.log(ul.children.length);
for (var i = 0;
i < ul.children.length;
i++) {
// 创建一个小li
var li = document.createElement('li');
// 记录当前小圆圈的索引号 通过自定义属性来做
li.setAttribute('index', i);
// 把小li插入到ol 里面
ol.appendChild(li);
// 4. 小圆圈的排他思想 我们可以直接在生成小圆圈的同时直接绑定点击事件
li.addEventListener('click', function () {
// 干掉所有人 把所有的小li 清除 current 类名
for (var i = 0;
i < ol.children.length;
i++) {
ol.children[i].className = '';
}
// 留下我自己当前的小li 设置current 类名
this.className = 'current';
// 5. 点击小圆圈,移动图片 当然移动的是 ul
// ul 的移动距离 小圆圈的索引号 乘以 图片的宽度 注意是负值
// 当我们点击了某个小li 就拿到当前小li 的索引号
var index = this.getAttribute('index');
// 当我们点击了某个小li 就要把这个li 的索引号给 num
num = index;
// 当我们点击了某个小li 就要把这个li 的索引号给 circle
circle = index;
// num = circle = index;
console.log(focusWidth);
console.log(index);
animate(ul, -index * focusWidth);
})
}// 把ol里面的第一个小li设置类名为 current
ol.children[0].className = 'current';
// 6. 克隆第一张图片(li)放到ul 最后面
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
// 7. 点击右侧按钮, 图片滚动一张
var num = 0;
// circle 控制小圆圈的播放
var circle = 0;
// flag 节流阀
var flag = true;
arrow_r.addEventListener('click', function () {
if (flag) {
flag = false;
// 关闭节流阀
// 如果走到了最后复制的一张图片,此时 我们的ul 要快速复原 left 改为 0
if (num == ul.children.length - 1) {
ul.style.left = 0;
num = 0;
}
num++;
animate(ul, -num * focusWidth, function () {
flag = true;
// 打开节流阀
});
// 8. 点击右侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放
circle++;
// 如果circle == 4 说明走到最后我们克隆的这张图片了 我们就复原
if (circle == ol.children.length) {
circle = 0;
}
// 调用函数
circleChange();
}
});
// 9. 左侧按钮做法
arrow_l.addEventListener('click', function () {
if (flag) {
flag = false;
if (num == 0) {
num = ul.children.length - 1;
ul.style.left = -num * focusWidth + 'px';
}
num--;
animate(ul, -num * focusWidth, function () {
flag = true;
});
// 点击左侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放
circle--;
// 如果circle < 0说明第一张图片,则小圆圈要改为第4个小圆圈(3)
// if (circle < 0) {
//circle = ol.children.length - 1;
// }
circle = circle < 0 ? ol.children.length - 1 : circle;
// 调用函数
circleChange();
}
});
function circleChange() {
// 先清除其余小圆圈的current类名
for (var i = 0;
i < ol.children.length;
i++) {
ol.children[i].className = '';
}
// 留下当前的小圆圈的current类名
ol.children[circle].className = 'current';
}// 10. 自动播放轮播图
var timer = setInterval(function () {
//手动调用点击事件
arrow_r.click();
}, 2000);
})
动画函数封装(注意不同于之前的另一种封装)
animate.js
function animate(obj, target, callback) {
// console.log(callback);
callback = function() {}调用的时候 callback()// 先清除以前的定时器,只保留当前的一个定时器执行
clearInterval(obj.timer);
obj.timer = setInterval(function () {
// 步长值写到定时器的里面
// 把我们步长值改为整数 不要出现小数的问题
// var step = Math.ceil((target - obj.offsetLeft) / 10);
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
// 停止动画 本质是停止定时器
clearInterval(obj.timer);
// 回调函数写到定时器结束里面
// if (callback) {
//// 调用函数
//callback();
// }
callback && callback();
}
// 把每次加1 这个步长值改为一个慢慢变小的值步长公式:(目标值 - 现在的位置) / 10
obj.style.left = obj.offsetLeft + step + 'px';
}, 15);
}
index.html
Document - 锐客网
>
* {
padding: 0;
margin: 0;
}a {
text-decoration: none;
}ul,
ol,
li {
list-style: none;
}
="./js/animate.js">
="./js/index.js">
index.css样式
@charset "uft-8";
.focus {
position: relative;
width: 721px;
height: 455px;
background-color: purple;
overflow: hidden;
margin: 0 auto;
}.focus ul {
position: absolute;
top: 0;
left: 0;
width: 600%;
}.focus ul li {
float: left;
}.arrow-l,
.arrow-r {
display: none;
position: absolute;
top: 50%;
margin-top: -20px;
width: 24px;
height: 40px;
background: rgba(0, 0, 0, .3);
text-align: center;
line-height: 40px;
color: #fff;
font-family: 'icomoon';
font-size: 18px;
z-index: 2;
}.arrow-r {
right: 0;
}.circle {
position: absolute;
bottom: 10px;
left: 50px;
}.circle li {
float: left;
width: 8px;
height: 8px;
/*background-color: #fff;
*/
border: 2px solid rgba(255, 255, 255, 0.5);
margin: 0 3px;
border-radius: 50%;
/*鼠标经过显示小手*/
cursor: pointer;
}.current {
background-color: #fff;
}
导航栏筋斗云效果
- 锐客网
>
* {
margin: 0;
padding: 0
}ul {
list-style: none;
}body {
background-color: black;
}.c-nav {
width: 900px;
height: 42px;
background: #fff url(images/rss.png) no-repeat right center;
margin: 100px auto;
border-radius: 5px;
position: relative;
}.c-nav ul {
position: absolute;
}.c-nav li {
float: left;
width: 83px;
text-align: center;
line-height: 42px;
}.c-nav li a {
color: #333;
text-decoration: none;
display: inline-block;
height: 42px;
}.c-nav li a:hover {
color: white;
}.c-nav li.current a {
color: #0dff1d;
}.cloud {
position: absolute;
left: 0;
top: 0;
width: 83px;
height: 42px;
background: url(images/cloud.gif) no-repeat;
}
>
window.addEventListener('load', function () {
// 1. 获取元素
var cloud = document.querySelector('.cloud');
var c_nav = document.querySelector('.c-nav');
var lis = c_nav.querySelectorAll('li');
// 2. 给所有的小li绑定事件
// 这个current 做为筋斗云的起始位置
var current = 0;
for (var i = 0;
i < lis.length;
i++) {
// (1) 鼠标经过把当前小li 的位置做为目标值
lis[i].addEventListener('mouseenter', function () {
animate(cloud, this.offsetLeft);
});
// (2) 鼠标离开就回到起始的位置
lis[i].addEventListener('mouseleave', function () {
animate(cloud, current);
});
// (3) 当我们鼠标点击,就把当前位置做为目标值
lis[i].addEventListener('click', function () {
current = this.offsetLeft;
});
}
})// 动画函数
function animate(obj, target, callback) {
// console.log(callback);
callback = function() {}调用的时候 callback()
// 先清除以前的定时器,只保留当前的一个定时器执行
clearInterval(obj.timer);
obj.timer = setInterval(function () {
// 步长值写到定时器的里面
// 把我们步长值改为整数 不要出现小数的问题
// var step = Math.ceil((target - obj.offsetLeft) / 10);
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
// 停止动画 本质是停止定时器
clearInterval(obj.timer);
// 回调函数写到定时器结束里面
// if (callback) {
//// 调用函数
//callback();
// }
callback && callback();
}
// 把每次加1 这个步长值改为一个慢慢变小的值步长公式:(目标值 - 现在的位置) / 10
obj.style.left = obj.offsetLeft + step + 'px';
}, 15);
}
推荐阅读
- javascript|JavaScript之setTimeout与setInterval的用法与区别
- javascript|原生JavaScript运动功能系列(一)(运动功能剖析与匀速运动实现)
- jquery|JavaScript之jQuery学习三部曲【下篇】
- javascript|JS动画封装
- 测试|软件测试 Web自动化测试 基础知识 HTML CSS JavaScript
- 前端|简单零基础学会H5移动端滑动翻页效果
- JavaScript|DOM操作CSS
- 对象|自己定义构造函数来实现tab栏效果以及this的指向问题
- 节流--防抖