新貨!如何制作一個高效輪播圖?

francis.y 7年前發布 | 12K 次閱讀 CSS JavaScript開發 JavaScript

輪播圖千種萬種,怎樣才能做出符合要求的輪播圖?原理上天入地,如何優化才能達到極限絲滑?本文作者將解答這一切,通過現場制作一個輪播圖,帶你詳細了解、理解,制作 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

 

 本文由用戶 francis.y 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!