轮播图千种万种,怎样才能做出符合要求的轮播图?原理上天入地,如何优化才能达到极限丝滑?本文作者将解答这一切,通过现场制作一个轮播图,带你详细了解、理解,制作 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ì) 、、。如果任何不足缺少,评论建议!谢