案例|动画之匀速加速减速缓冲运动(加轮播图)


动画

  • 运动
    • 匀速运动
    • 加速匀速
    • 减速运动
    • 缓冲运动(在某个区间内做减速运动)
    • 封装缓冲运动的函数
    • 需求:当定时器全部清除完时(这里有多个定时器),让元素背景改为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); }

      推荐阅读