新貨!如何制作一個高效輪播圖?
輪播圖千種萬種,怎樣才能做出符合要求的輪播圖?原理上天入地,如何優化才能達到極限絲滑?本文作者將解答這一切,通過現場制作一個輪播圖,帶你詳細了解、理解,制作 All kinds of 高性能輪播圖 !
本文共分 首尾相接地輪播、高級選項[加載優化, 突出焦點, 有限輪播, 位置指示] 兩個板塊。你可以在高級選項 中為自己的輪播設置添加 單獨的特性和功能。盡管用, 所有的 ,所有的,所有的(重要的事說三遍),作者都已竭盡全力做到高效!
另外,在事實上,輪播圖的點擊率通常都很低,很少能引起用戶的注意,而卻往往占用了頁面某個極重要的位置。你的網站 真的 需要一個輪播圖嗎?輕輕問自己三聲,谷歌一下對輪播圖效果的相關調查和建議,再決定是否要著手制作你的輪播圖。
首尾相接地輪播~
這里原理非常巧妙,是作者在Google Play 找到的方法。如果你不需要這種可以單方向無限輪播的圖,那么可以在了解大概后去 ↓ 高級選項 中找到 有限輪播 進行相關更改。
1. 包裹圖片
性能不允許我們為每一張圖片計算位置,同樣我們并沒有這個必要。我們要一個父元素 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 加速。欲知詳情,請谷歌。
2. 方向規定
.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>
3. 重要的 JS
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
}
4. 整理代碼
<!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"http://添加的代碼
setTimeout(()=>{
father.style.transform=`translate3d(${askX(to)}px, 0, 0)`
sons[showingId].className=""http://添加的還有這
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ì) 、、。如果任何不足缺少,評論建議!謝
來自:https://segmentfault.com/a/1190000008255568