🏍️回调地狱
🍇具体分析
根据上文动画函数的讲解,今天更近一步加深对回调函数的使用。
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><style>div { width: 200px; height: 200px; background: #f00;margin-top: 30px; position: absolute; } </style></head><body><button>测试按钮</button><div></div></body></html>
通过页面的内容,我想让盒子一次性实现向左移动然后变宽然后变高然后向下移动然后向上移动,然后向左移动。。。 如何做???,这里我们就需要借助回调函数不断调用定时器移动,通俗点讲,我们讲多个回调函数一起使用称为“回调地狱”。
写JS第一步通常就是获取DOM元素,但是接下来的回调函数如何书写,请看如下代码及注释:
<script>varbtn=document.querySelector('button') vardiv=document.querySelector('div') functionmove (obj, attr, target, speed, callback) { // 关闭上一个定时器clearInterval(obj.timer); // 获取元素当前位置varcurrent=parseInt(getStyle(obj, attr)); // 判断速度的正负值if (current>target) { // 此时速度应为负值speed=-speed; } // 开启一个定时器,用来执行动画效果// 向执行动画的对象添加一个timer属性,用来保存它自己的定时器obj.timer=setInterval(function () { // 获取目标原来的left值varoldValue=parseInt(getStyle(obj, attr)); // 在旧值的基础上增加varnewValue=oldValue+speed; /* 向左移动时,需要判断newValue是否小于target 向右移动时,需要判断newValue是否大于target */if ((speed<0&&newValue<target) || (speed>0&&newValue>target)) { newValue=target; } // 将新值赋予目标obj.style[attr] =newValue+'px'; // 当元素移动到target时,停止执行动画if (newValue==target) { // 达到目标关闭定时器clearInterval(obj.timer); // 动画执行完毕调用回调函数 callbackcallback&&callback(); } }, 30); } //定义一个函数,用来获取指定元素的当前样式functiongetStyle (obj, name) { if (window.getComputedStyle) { // 正常浏览器的方式,具有getComputedStyle()方法returngetComputedStyle(obj, null)[name]; } else { // IE8的方式,没有getComputedStyle()方法returnobj.currentStyle[name]; } } </script>
现在分别对代码里面的参数进行讲解:
move参数:
* obj:要执行动画的对象
* attr:要执行动画的样式
* target:执行动画的目标位置
* speed:移动的速度(正数向右移,负数向左移)
* callback:回调函数,这个函数将会在动画执行完毕以后执行
getStyle参数:
* obj:要获取样式的元素
* name:要获取的样式名
定时器书写完毕,我们开始调用move函数,可以看到move里面的callback回调函数里面在嵌套move,接着在回调函数里面在嵌套move,不停的在callback里面嵌套函数,达到连续的动画效果,称为“回调函数”,可以看一下效果,如下:
btn.addEventListener('click', function () { move(div, "left", 300, 10, function () { move(div,"width",500,10,function() { move(div,"height",300,10,function(){ move(div,"bottom",100,10,function(){ move(div,"top",100,10,function(){ move(div,"left",-200,10,function(){}) }) }) }) }) }) })
通过画面可以看到非常丝滑的动画效果,在这我们就要思考一下,是否可以把这个动画函数封装在一个JS文件里,当我们想要实现动画效果时直接调用动画函数就可以了,答案是“可行的”。这样便于了代码的书写,大大提高了编程效率。接下来通过一个典型案例,在进行动画函数的学习。
🍈完整代码
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><style>div { width: 200px; height: 200px; background: #f00;margin-top: 30px; position: absolute; } </style></head><body><button>测试按钮</button><div></div><script>varbtn=document.querySelector('button') vardiv=document.querySelector('div') functionmove (obj, attr, target, speed, callback) { // 关闭上一个定时器clearInterval(obj.timer); // 获取元素当前位置varcurrent=parseInt(getStyle(obj, attr)); // 判断速度的正负值if (current>target) { // 此时速度应为负值speed=-speed; } // 开启一个定时器,用来执行动画效果// 向执行动画的对象添加一个timer属性,用来保存它自己的定时器obj.timer=setInterval(function () { // 获取目标原来的left值varoldValue=parseInt(getStyle(obj, attr)); // 在旧值的基础上增加varnewValue=oldValue+speed; /* 向左移动时,需要判断newValue是否小于target 向右移动时,需要判断newValue是否大于target */if ((speed<0&&newValue<target) || (speed>0&&newValue>target)) { newValue=target; } // 将新值赋予目标obj.style[attr] =newValue+'px'; // 当元素移动到target时,停止执行动画if (newValue==target) { // 达到目标关闭定时器clearInterval(obj.timer); // 动画执行完毕调用回调函数 callbackcallback&&callback(); } }, 30); } functiongetStyle (obj, name) { if (window.getComputedStyle) { // 正常浏览器的方式,具有getComputedStyle()方法returngetComputedStyle(obj, null)[name]; } else { // IE8的方式,没有getComputedStyle()方法returnobj.currentStyle[name]; } } btn.addEventListener('click', function () { move(div, "left", 300, 10, function () { move(div,"width",500,10,function() { move(div,"height",300,10,function(){ move(div,"bottom",100,10,function(){ move(div,"top",100,10,function(){ move(div,"left",-200,10,function(){}) }) }) }) }) }) }) </script></body></html>
🏃网页轮播图
轮播图也称为焦点图,是网页中比较常见的特效,今天做一下这个典型案例。
🍉制作轮播图的空间盒子,用来存放图片
我们可以根据自己的喜好设置自己轮播图的相关样式,这里就就需要借助HTML和CSS了,讲真HTML和CSS是重点基础,如果这两个没学好,是制作不出自己想要的样式,更别提后面的JS以及相关框架了,基础才是最重要的。
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><style>*{ margin: 0; padding: 0; } /* 设置outer样式 */#outer{/* 设置宽高 */width: 820px; height: 452px; /* 居中 */margin: 50pxauto; /* 设置背景颜色 */background-color: #008c8c;/* 设置padding */padding: 10px0; /* 开启相对定位 */position: relative; /* 溢出隐藏 */overflow: hidden; } /* 设置imgList */#imgList{/* 去除项目符号 */list-style: none; /* 设置ul宽度 *//* width: 3280px; *//* 开启绝对定位 */position: absolute; /* 设置偏移量,每向左移动820px 就会显示下一张图片 */ } /* 设置li */#imgList li{/* 设置浮动 */float: left; /* 设置左右外边距 */margin: 010px; } /* 设置导航按钮 */ .navDiv{ /* 开启绝对定位 */position: absolute; /* 设置位置 */bottom: 20px; /* left: 35%; */ } .navDiva{ /* 设置浮动,浮动之后,行内元素就能设置宽高 */float: left; /* 设置宽高 */width: 25px; height: 25px; /* 设置背景颜色 */background-color: #f00;/* 设置左右外边距 */margin: 015px; /* 设置半透明 */opacity: .5; /* 设置圆角 */border-radius: 50%; } /* 设置鼠标移入效果 */ .navDiva:hover{ background-color: green; } </style></head><body><!--创建一个外部的div作为一个大的容器--><divid="outer"><!--创建一个ul,用于放置图片--><ulid="imgList"><li><imgsrc="./01.jpeg"></li><li><imgsrc="./02.jpeg"></li><li><imgsrc="./03.jpeg"></li><li><imgsrc="./04.jpeg"></li><li><imgsrc="./01.jpeg"></li></ul><!--设置导航按钮--><divclass="navDiv"><ahref="javascript:;"></a><ahref="javascript:;"></a><ahref="javascript:;"></a><ahref="javascript:;"></a></div></div></body></html>
这里需要注意两点:
轮播图的按钮是要通过JS来居中,因为如果借助CSS居中,就会写死,后期添加图片又要重新设置。
javascript:;是一个伪协议,加在超级链接上就可以防止 链接跳转,从而可以给链接JS上写自己想执行的代码。javascript:void(0) 表示一个死链接,执行空事件。
🍊点击小圆圈可以播放相应的图片
<script>window.addEventListener('load',function(){ // 获取ul标签varul=document.querySelector('#imgList') // 获取ul下li的子元素的个数,并给ul动态赋予宽度值ul.style.width=ul.children.length*820+'px'; // 设置导航按钮动态居中varnavDiv=document.querySelector('.navDiv'); varouter=document.querySelector('#outer'); navDiv.style.left= (outer.offsetWidth-navDiv.offsetWidth) /2+'px'; // 默认显示图片的索引varindex=0; // 获取所有的avaraAll=document.querySelector('.navDiv').querySelectorAll('a'); // 为所有超链接绑定响应函数for(vari=0;i<aAll.length;i++){ // 为每一个超链接都添加一个num属性aAll[i].num=i; // 为超链接绑定点击响应函数aAll[i].addEventListener('click',function(){ // 获取超链接的索引,并将其设置为indexindex=this.num; // 切换图片imgList.style.left=-820*index+'px'; setA(); }) } // 创建一个方法用来选中afunctionsetA(){ for(vari=0;i<aAll.length;i++){ aAll[i].style.backgroundColor=''; } // 创建点击时的颜色aAll[index].style.backgroundColor='green'; } }) </script>
通过JS书写动态给按钮进行居中,并设置一个点击事件,来进行图片的切换并且按钮样式虽图片的变化而变化,这里借用了“排他思想”,先干掉所有人,然后保留自己人。
🍋鼠标不点击,轮播图会自动播放
自动播放就涉及动画函数了,根据上文的回调地狱的讲解,将动画函数move进行封装。如下:
functionmove(obj,attr,target,speed,callback){ // 关闭上一个定时器clearInterval(obj.timer); // 获取元素当前位置varcurrent=parseInt(getStyle(obj,attr)); // 判断速度的正负值if(current>target){ // 此时速度应为负值speed=-speed; } // 开启一个定时器,用来执行动画效果// 向执行动画的对象添加一个timer属性,用来保存它自己的定时器obj.timer=setInterval(function(){ // 获取目标原来的left值varoldValue=parseInt(getStyle(obj,attr)); // 在旧值的基础上增加varnewValue=oldValue+speed; /* 向左移动时,需要判断newValue是否小于target 向右移动时,需要判断newValue是否大于target */if((speed<0&&newValue<target) || (speed>0&&newValue>target)){ newValue=target; } // 将新值赋予目标obj.style[attr] =newValue+'px'; // 当元素移动到target时,停止执行动画if(newValue==target){ // 达到目标关闭定时器clearInterval(obj.timer); // 动画执行完毕调用回调函数 callbackcallback&&callback(); } },30); } functiongetStyle(obj,name){ if(window.getComputedStyle){ // 正常浏览器的方式,具有getComputedStyle()方法returngetComputedStyle(obj,null)[name]; }else{ // IE8的方式,没有getComputedStyle()方法returnobj.currentStyle[name]; } }
接下里通过引入move函数实现动画效果
<script>window.addEventListener('load',function(){ // 获取ul标签varul=document.querySelector('#imgList') // 获取ul下li的子元素的个数,并给ul动态赋予宽度值ul.style.width=ul.children.length*820+'px'; // 设置导航按钮动态居中varnavDiv=document.querySelector('.navDiv'); varouter=document.querySelector('#outer'); navDiv.style.left= (outer.offsetWidth-navDiv.offsetWidth) /2+'px'; // 默认显示图片的索引varindex=0; // 获取所有的avaraAll=document.querySelector('.navDiv').querySelectorAll('a'); // 设置默认选中效果aAll[index].style.backgroundColor='green'; // 为所有超链接绑定响应函数for(vari=0;i<aAll.length;i++){ // 为每一个超链接都添加一个num属性aAll[i].num=i; // 为超链接绑定点击响应函数aAll[i].addEventListener('click',function(){ //关闭自动切换的定时器clearInterval(stop); // 获取超链接的索引,并将其设置为indexindex=this.num; // 切换图片// imgList.style.left = -820*index + 'px';setA(); move(imgList,"left",-820*index,20,function(){ //动画执行完毕,开启自动切换autoChange(); }) }) } // // 开启自动切换图片autoChange(); // 创建一个方法用来选中afunctionsetA(){ // 判断当前索引是否是第一张if(index>=ul.children.length-1){ // 则将index设置为0index=0; // 此时显示最后一张图片,而最后一张图片和第一张一模一样// 通过CSS将最后一张切换成第一张imgList.style.left=0+'px'; } for(vari=0;i<aAll.length;i++){ aAll[i].style.backgroundColor=''; } // 创建点击时的颜色aAll[index].style.backgroundColor='green'; } // 创建一个函数,用来开启自动切换图片// 定义一个自动切换的定时器的标识varstop; functionautoChange(){ // 开启一个定时器,用来定时切换图片stop=setInterval(function(){ // 使索引自增index++; // 判断index的值index%=ul.children.length; // 执行动画来切换图片move(imgList,"left",-820*index,20,function(){ // 设置导航点setA(); }) },3000); }; }) </script><scriptsrc="./move.js"></script>
可以看到代码中,我们设计了一个定时器实现自动切换图片,并设计在点击函数中关闭定时器,这样就不会出现点击事件和自动播放发生冲突,造成图片的怪异播放现象。
🍌完整代码
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><style>*{ margin: 0; padding: 0; } /* 设置outer样式 */#outer{/* 设置宽高 */width: 820px; height: 452px; /* 居中 */margin: 50pxauto; /* 设置背景颜色 */background-color: #008c8c;/* 设置padding */padding: 10px0; /* 开启相对定位 */position: relative; /* 溢出隐藏 */overflow: hidden; } /* 设置imgList */#imgList{/* 去除项目符号 */list-style: none; /* 设置ul宽度 *//* width: 3280px; *//* 开启绝对定位 */position: absolute; /* 设置偏移量,每向左移动820px 就会显示下一张图片 */ } /* 设置li */#imgList li{/* 设置浮动 */float: left; /* 设置左右外边距 */margin: 010px; } /* 设置导航按钮 */ .navDiv{ /* 开启绝对定位 */position: absolute; /* 设置位置 */bottom: 20px; /* left: 35%; */ } .navDiva{ /* 设置浮动,浮动之后,行内元素就能设置宽高 */float: left; /* 设置宽高 */width: 25px; height: 25px; /* 设置背景颜色 */background-color: #f00;/* 设置左右外边距 */margin: 015px; /* 设置半透明 */opacity: .5; /* 设置圆角 */border-radius: 50%; } /* 设置鼠标移入效果 */ .navDiva:hover{ background-color: green; } </style><script>window.addEventListener('load',function(){ // 获取ul标签varul=document.querySelector('#imgList') // 获取ul下li的子元素的个数,并给ul动态赋予宽度值ul.style.width=ul.children.length*820+'px'; // 设置导航按钮动态居中varnavDiv=document.querySelector('.navDiv'); varouter=document.querySelector('#outer'); navDiv.style.left= (outer.offsetWidth-navDiv.offsetWidth) /2+'px'; // 默认显示图片的索引varindex=0; // 获取所有的avaraAll=document.querySelector('.navDiv').querySelectorAll('a'); // 设置默认选中效果aAll[index].style.backgroundColor='green'; // 为所有超链接绑定响应函数for(vari=0;i<aAll.length;i++){ // 为每一个超链接都添加一个num属性aAll[i].num=i; // 为超链接绑定点击响应函数aAll[i].addEventListener('click',function(){ //关闭自动切换的定时器clearInterval(stop); // 获取超链接的索引,并将其设置为indexindex=this.num; // 切换图片// imgList.style.left = -820*index + 'px';setA(); move(imgList,"left",-820*index,20,function(){ //动画执行完毕,开启自动切换autoChange(); }) }) } // // 开启自动切换图片autoChange(); // 创建一个方法用来选中afunctionsetA(){ // 判断当前索引是否是第一张if(index>=ul.children.length-1){ // 则将index设置为0index=0; // 此时显示最后一张图片,而最后一张图片和第一张一模一样// 通过CSS将最后一张切换成第一张imgList.style.left=0+'px'; } for(vari=0;i<aAll.length;i++){ aAll[i].style.backgroundColor=''; } // 创建点击时的颜色aAll[index].style.backgroundColor='green'; } // 创建一个函数,用来开启自动切换图片// 定义一个自动切换的定时器的标识varstop; functionautoChange(){ // 开启一个定时器,用来定时切换图片stop=setInterval(function(){ // 使索引自增index++; // 判断index的值index%=ul.children.length; // 执行动画来切换图片move(imgList,"left",-820*index,20,function(){ // 设置导航点setA(); }) },3000); }; }) </script><scriptsrc="./tool.js"></script></head><body><!--创建一个外部的div作为一个大的容器--><divid="outer"><!--创建一个ul,用于放置图片--><ulid="imgList"><li><imgsrc="./01.jpeg"></li><li><imgsrc="./02.jpeg"></li><li><imgsrc="./03.jpeg"></li><li><imgsrc="./04.jpeg"></li><li><imgsrc="./01.jpeg"></li></ul><!--设置导航按钮--><divclass="navDiv"><ahref="javascript:;"></a><ahref="javascript:;"></a><ahref="javascript:;"></a><ahref="javascript:;"></a></div></div></body></html>