轮播图千种万种,怎样才能做出符合要求的轮播图?原理上天入地,如何优化才能达到极限丝滑?本文作者将解答这一切,通过现场制作一个轮播图,带你详细了解、理解,制作 All kinds of 高性能轮播图 !
本文共分 首尾相接地轮播、高级选项[加载优化, 突出焦点, 有限轮播, 位置指示] 两个板块。你可以在高级选项 中为自己的轮播设置添加 单独的特性和功能。尽管用, 所有的 ,所有的,所有的(重要的事说三遍),作者都已竭尽全力做到高效!
另外,在事实上,轮播图的点击率通常都很低,很少能引起用户的注意,而却往往占用了页面某个极重要的位置。你的网站 真的 需要一个轮播图吗?轻轻问自己三声,谷歌一下对轮播图效果的相关调查和建议,再决定是否要着手制作你的轮播图。
这里原理非常巧妙,是作者在Google Play 找到的方法。如果你不需要这种可以单方向无限轮播的图,那么可以在了解大概后去 ↓ 高级选项 中找到 有限轮播 进行相关更改。
性能不允许我们为每一张图片计算位置,同样我们并没有这个必要。我们要一个父元素 div.father
来含括,用 .father > div
对图片设置外观。 div.viewport
作为可见部分,是爸爸的爸爸,帮他儿子隐藏不出场的图片。嗯,就像这样
.viewport { width: 900px;height: 300px; /* 可视宽,图片高 */ overflow: hidden; /* 超出隐藏 */ position: relative /* 为下文妈妈的指导位置作准备 */ } .father { height: inherit; /* 图片高 */ width: 3000%; /* 为了大,子元素 float 无法将其撑开 */ transform: translate3d(0, 0, 0);transition: transform 0.5s ease-in-out /* 丝滑流畅 */ } .father > div { width: 550px;height: inherit; /* 自定义 */ float: left /* 排起来 */ }
这里面的丝滑流畅是因为 transform: translate3d()
为元素开启了GPU 加速。欲知详情,请谷歌。
.father
用 transform: translate3d()
帮儿子们完成整体移动。定时调整位置是不被推荐的,不让用户切换是不可能的。要 .mother
作为爸爸的方向标,她指导爸爸。
.mother { width: 30px;height: inherit; /* 宽高 */ line-height: 300px;text-align: center; /* 居中 */ cursor: pointer;user-select:none; /* 可点击,不可选择 */ background: rgba(0,0,0,0.15); /* 自定义 */ position: absolute;top: 0; /* 位置 */ } .mother[left] {left: 0} .mother[right] {right: 0}
<div class="viewport"> <div class="father" id="father"> <div>A</div><!-- 1 --> <div>B</div> <div>C</div><!-- 3 --> <div>D</div> <div>E</div><!-- 5 --> </div> <div class="mother" id="left" left>^_^</div> <div class="mother" id="right" right>:-)</div> <!-- 我可没说有两 wife...只是她指导老爸一会儿向东,一会儿向西,抓不着方向 ~~ --> </div>
var showingId; //缓存轮播的焦点。从0 开始,和JS 的下标号[] 一样 var fatherGo=to=>{} // Mom's order. document.getElementById("left").onclick= ()=>{fatherGo(showingId-1)} document.getElementById("right").onclick= ()=>{fatherGo(showingId+1)}
页面加载时:选取一张作为焦点
切换时: fatherGo(to)
将负责跳转到指定的焦点图;
高效、首尾相接地轮播( 有限轮播 见 ↓ 高级选项)
这两个是思路,也是 题点 。对第一题,可以这样解决:
var father=document.getElementById("father"), sons=father.children, width=550, //自己设的CSS, 如果图有margin, 要加上(如 margin: 0 20 则 550+20*2) outerWidth=(900-width)/2, // (可视宽-焦点宽)/2,好理解吧,只显示了一部分的图片宽度 showingId=parseInt(sons.length/2)-1; //除法求整数部分,并保证对应JS 中代号 var askX = id => -id*width+outerWidth; var fatherGo = to => { father.style.transform=`translate3d(${askX(to)}px, 0, 0)` showingId=to } fatherGo(showingId)
要把Id 为2 的儿子作焦点,即第3 个儿子,我们可以向左挪过去2 个儿子(这时候 儿子.Id=2
就靠在了最左边),再加回 outerWidth
—— ask(id)
函数原理。
第二点。作两个儿子们的分身,在移动到 差点到 首尾连接处时 关闭动画 挪到另一个分身相应位置~…
<style> .moving {transition: none} /* 关闭动画样式 */ </style> <div class="father" id="father"> <div>A</div><div>B</div><div>C</div><div>D</div><div>E</div> <div>A</div><!-- 1 --> <div>B</div> <div>C</div><!-- 3 --> <div>D</div> <div>E</div><!-- 5 --> <div>A</div><div>B</div><div>C</div><div>D</div><div>E</div> </div>
//上面的代码 var father=document.getElementById("father"), sons=father.children, width=550, outerWidth=(900-width)/2, showingId=parseInt(sons.length/2)-1; //不变 var askX = id => -id*width+outerWidth; var fatherGo = to => { father.style.transform=`translate3d(${askX(to)}px, 0, 0)` showingId=to } fatherGo(showingId) //此步的更改 - 开动你的大脑!! var closeLeft=1, closeRight=sons.length-2, //这两变量表示差一点到 “首尾相接处” 的图片Id。忽略拙略的命名 //Left: 第2 张图,Id 为1; Right: 倒数第二张,Id 为总数-2 toLeft=sons.length/3*2+1, toRight=sons.length/3-2, //这两表示当运动到上面两个Id 图片时应悄悄跳转到的分身Id time=new Date();//防止多次跳转造成下面setTimeout 混乱 fatherGo = to => { var newDate=new Date() if(newDate-time<600)return; time=newDate //↑ 防止多次跳转造成下面setTimeout 混乱 father.style.transform=`translate3d(${askX(to)}px, 0, 0)`; showingId=to if(to==closeLeft){to=toLeft} else if(to==closeRight){to=toRight} else {return} setTimeout(()=>{ father.classList.add("moving") setTimeout(()=>{ father.style.transform=`translate3d(${askX(to)}px, 0, 0)` setTimeout(()=>{ father.classList.remove("moving") },50); },30) },500);//translation 的时间 showingId=to }
<!DOCTYPE html><html> <head><title>轮播图~~ 转呀转</title> <style> html,body {height: 100%} .viewport { width: 900px;height: 300px; /* 可视宽,图片高 */ overflow: hidden; /* 超出隐藏 */ position: relative /* 为下文妈妈的指导位置作准备 */ } .father { height: inherit; /* 图片高 */ width: 3000%; /* 为了大,子元素 float 无法将其撑开 */ transform: translate3d(0, 0, 0);transition: transform 0.5s ease-in-out /* 丝滑流畅 */ } .father > div { width: 550px;height: inherit;background: #aaa; /* 自定义 */ float: left /* 排起来 */ } .mother { width: 30px;height: inherit; /* 宽高 */ line-height: 300px;text-align: center; /* 居中 */ cursor: pointer;user-select:none; /* 可点击,不可选择 */ background: rgba(0,0,0,0.15); /* 自定义 */ position: absolute;top: 0; /* 位置 */ } .mother[left] {left: 0} .mother[right] {right: 0} .moving {transition: none} </style> </head> <body> <div class="viewport"> <div class="father" id="father"> <div>A</div><div>B</div><div>C</div><div>D</div><div>E</div> <div>A</div><!-- 1 --> <div>B</div> <div>C</div><!-- 3 --> <div>D</div> <div>E</div><!-- 5 --> <div>A</div><div>B</div><div>C</div><div>D</div><div>E</div> </div> <div class="mother" id="left" left>^_^</div> <div class="mother" id="right" right>:-)</div> </div> <script> var father=document.getElementById("father"), sons=father.children, width=550,outerWidth=(900-width)/2, showingId=parseInt(sons.length/2)-1, askX = id => -id*width+outerWidth, closeLeft=1, closeRight=sons.length-2, toLeft=sons.length/3*2+1, toRight=sons.length/3-2, time=new Date(); father.style.transform=`translate3d(${askX(showingId)}px, 0, 0)` //加载页面时移动 var fatherGo = to => { var newDate=new Date() if(newDate-time<600)return; time=newDate father.style.transform=`translate3d(${askX(to)}px, 0, 0)`; showingId=to if(to==closeLeft){to=toLeft} else if(to==closeRight){to=toRight} else {return} setTimeout(()=>{ father.classList.add("moving") setTimeout(()=>{ father.style.transform=`translate3d(${askX(to)}px, 0, 0)` setTimeout(()=>{ father.classList.remove("moving") },50); },30) },500);//translation 的时间 showingId=to } document.getElementById("left").onclick= ()=>{fatherGo(showingId-1)} document.getElementById("right").onclick= ()=>{fatherGo(showingId+1)} </script> </body></html>
代码通过了测试。你可能需要码更多的代码,以兼容各个浏览器。
最 激动人心 的地方终于到了!
一味地把 <script>
放到 </body>
前只会 适得其反 ——你需要 “加载优化” ;焦点图没有 特别样式 不够突出——你在想 “突出焦点” ;上级的要求不能 没头没尾 ——去看看 “有限轮播” ……所有的所有,尽在这里找到!
一味地把 <script>
放到 </body>
前只会适得其反。我们会在页面载入后看到轮播图转到焦点——这是非常有损体验的。其实可以把 一部分 <script>
放到 <head>
里面,或者轮播图前,阻塞DOM 的渲染。最有效的是提前计算好translateX 值,放到 style=""
。
<div class="father" id="father" style="transform: translate3d(-3125px, 0px, 0px);">...</div> <!-- -3125: 自己用前面的AskX 函数算 -->
最后删去JS 中多余的加载页面时移动 代码。
为了世界和平,后面的都这样优化了。
如何要用户Focus on 你的焦点?
/* 我们想要一个这样的效果 */ 焦点 {放大到110% } 其他 {半透明;正常大小}
没什么不对的。
.focusing {opacity: 1;transform: scale3d(1.1, 1.1, 1)/*3D 用于GPU 加速 */} .father > div {opacity: 0.4;background: #bbb;transition: transform 0.6s ease-in-out} .father.moving > div {transition: none}
为了不让用户在 “转换分身” 时看出来,得添加上面的第三个CSS。而JS 中,我们需要为 fatherGo(to)
函数添加更改焦点的方法,HTML 中提前为中心焦点Foucs 啦啦啦
fatherGo = to => { var newDate=new Date() if(newDate-time<600)return; time=newDate father.style.transform=`translate3d(${askX(to)}px, 0, 0)` sons[showingId].classList.remove("focusing") sons[to].classList.add("focusing") showingId=to if(to==closeLeft){to=toLeft} else if(to==closeRight){to=toRight} else {return} setTimeout(()=>{ father.classList.add("moving") sons[to].className="focusing"//添加的代码 setTimeout(()=>{ father.style.transform=`translate3d(${askX(to)}px, 0, 0)` sons[showingId].className=""//添加的还有这 setTimeout(()=>{ father.classList.remove("moving") },50); },30) },500); showingId=to }
<div class="father" id="father" style="transform: translate3d(-3125px, 0px, 0px);"> ... <div class="focusing">..</div><!--提前算好的焦点(可参考使用前面showingId 算法 --> ... </div>
这么简单的轮播,没压力啊。下面是总的HTML ,注释表示变化
<!DOCTYPE html><html> <head><title>轮播图~~ 转呀转</title> <style> html,body {height: 100%} .viewport { width: 900px;height: 300px; overflow: hidden; position: relative } .father { height: inherit; width: 3000%; transform: translate3d(0, 0, 0);transition: transform 0.5s ease-in-out } .father > div { width: 550px;height: inherit;background: #aaa; float: left } .mother { width: 30px;height: inherit; line-height: 300px;text-align: center; cursor: pointer;user-select:none; background: rgba(0,0,0,0.15); position: absolute;top: 0; } .mother[left] {left: 0} .mother[right] {right: 0} .moving {transition: none} </style> </head> <body> <div class="viewport"> <div class="father" id="father" style="transform: translate3d(-375px, 0px, 0px);"> <div>A</div><!-- 1 --> <div>B</div> <div>C</div><!-- 3 --> <div>D</div> <div>E</div><!-- 5 --> </div> <div class="mother" id="left" left>^_^</div> <div class="mother" id="right" right>:-)</div> </div> <script> var father=document.getElementById("father"), sons=father.children, width=550,outerWidth=(900-width)/2, showingId=parseInt(sons.length/2)-1, askX = id => -id*width+outerWidth, closeLeft=0, closeRight=sons.length-1,//这两变量被改了。删掉了toLeft 以及toRight left=document.getElementById('left'), right=document.getElementById("right");//缓存DOM var fatherGo = to => { if(to==closeLeft-1 || to==closeRight+1)return;//添加了超出有限范围的判断 //不再需要避免连击(因为没有了setTimeout) if(to==closeLeft){ father.style.transform="translate3d(0, 0, 0)"; left.classList.add("aClass")//自己看要不要给已经不能滑动的.mother 特殊样式 } else if(to==closeRight){ father.style.transform=`translate3d(${-closeRight*width+2*outerWidth}px, 0, 0)`; right.classList.add("aClass")//同上。给不给已经不能滑动的.mother 特殊样式 } else{ father.style.transform=`translate3d(${askX(to)}px, 0, 0)`; left.classList.remove("aClass");right.classList.remove("someClass")//你懂删不删 } //有限判断 //sons[showingId].className="" //sons[to].className="yourClass" //如果你需要 “突出焦点” ,还原这里的代码 showingId=to //删去无用代码 } left.onclick= ()=>{fatherGo(showingId-1)}//缓存了变量,可直接绑定 right.onclick= ()=>{fatherGo(showingId+1)} </script> </body></html>
需与 “有限滚动” 依存。总的HTML 在这
<!DOCTYPE html><html> <head><title>轮播图~~ 转呀转</title> <style> html,body {height: 100%} .viewport { width: 900px;height: 300px; overflow: hidden; position: relative } .father { height: inherit; width: 3000%; transform: translate3d(0, 0, 0);transition: transform 0.5s ease-in-out } .father.moving {transition: none} .father > div { width: 550px;height: inherit;background: #aaa; float: left } .mother { width: 30px;height: inherit; line-height: 300px;text-align: center; cursor: pointer;user-select:none; background: rgba(0,0,0,0.4); position: absolute;top: 0; } .mother[left] {left: 0} .mother[right] {right: 0} .mother.close {opacity: 0.3;transition: opacity 0.6s ease} .seter { width: 400px;height: 20px; position: absolute;bottom: 0;left: calc(50% - 200px); cursor: pointer; } .seter > div { width: 80px;height: 28px; background: orange; float: left; } .seter > div.on {margin-top: -8px;transition: margin 0.5s ease-in-out} </style> </head> <body> <div class="viewport"> <div class="father" id="father" style="transform: translate3d(-375px, 0px, 0px);"> <div>A</div><!-- 1 --> <div>B</div> <div>C</div><!-- 3 --> <div>D</div> <div>E</div><!-- 5 --> </div> <div class="seter" id="seter"> <div id="0"></div> <div class="on" id="1"></div> <div id="2"></div> <div id="3"></div> <div id="4"></div> </div> <div class="mother" id="left" left>^_^</div> <div class="mother" id="right" right>:-)</div> </div> <script> var father=document.getElementById("father"), sons=father.children, width=550,outerWidth=(900-width)/2, showingId=parseInt(sons.length/2)-1, askX = id => -id*width+outerWidth, closeLeft=0, closeRight=sons.length-1, left=document.getElementById('left'), right=document.getElementById("right"), seter=document.getElementById("seter"), seters=seter.children; var fatherGo = to => { if(to==closeLeft-1 || to==closeRight+1)return; if(to==closeLeft){ father.style.transform="translate3d(0, 0, 0)"; left.classList.add("close") } else if(to==closeRight){ father.style.transform=`translate3d(${-closeRight*width+2*outerWidth}px, 0, 0)`; right.classList.add("close") } else{ father.style.transform=`translate3d(${askX(to)}px, 0, 0)`; left.classList.remove("close");right.classList.remove("close") } seters[showingId].className="" seters[to].className="on" //sons[showingId].className="" //sons[to].className="yourClass" //如果你需要 “突出焦点” ,还原这里的代码 showingId=to } left.onclick= ()=>{fatherGo(showingId-1)} right.onclick= ()=>{fatherGo(showingId+1)} seter.onclick= e=>{ fatherGo(Number(e.target.id)) } </script> </body></html>
之前发了一次。不知什么问题,文章可以显示在列表中,但除作者外无法访问?累(luì) 、、。如果任何不足缺少,评论建议!谢