寫在前面:最近寫了個3d輪播效果圖,在此將思路和過程中遇到的問題都記錄下來。
首先,我們下來了解一下perspective和transform都是做什么的。
transform -- css3屬性,可以對元素進行變換(2d/3d),包括平移translate,旋轉rotate,縮放scale,等等(完整取值參考 W3C)。
perspective -- css3屬性,當元素涉及3d變換時,perspective可以定義我們眼睛看到的3d立體效果,即空間感。通俗地解釋就比如你去電影院看電影,你距離大熒幕的距離就相當於perspective的值啦,離得越遠則perspective值越大,看到空間效果也就會不一樣!
接下來,我們就進入正題了。
先上3d輪播圖效果圖:
圖一
圖二
圖三
***圖一為前一張輪播圖,圖二是輪播圖前后切換時的3d效果圖,圖三為后一張輪播圖***
實現上面的效果,需要三層頁面結構:
1. 最外面的容器.swiper-wrapper,即圖中天藍色邊框這個部分。為這部分設置關鍵css如下:
transform-style: preserve-3d; //3d旋轉效果
perspective: 1000px; //3d立體空間感
perspective-origin: 50% 50%; //觀察視角, 50% 50%代表從中間觀察
2. 用ul包裹的li多邊體。參考圖二可知,我們的每張圖片實際上是由n個片段拼接而成,這是怎么實現的呢?很簡單,多個li + background-position定位輕松實現啦;
3. li里的div數組,數組的長度等於輪播圖的總張數。這個div數組將所有的圖片圍繞x軸形成了一個多邊體。完整多邊體效果如圖四:
圖四
那么怎么實現這個多邊體呢?這就要用到今天的transform屬性了。假設一個多邊體有m條邊,每個div平面代表一條邊(即每張圖片的1/n),我們先將每個div平面絕對定位到li的左上角(left:0;top:0;),此時的每個div平面都在x軸與y軸形成的平面上,也就是電腦屏幕平面上。接下來我們將每個div平面依次進行變換transform: rotateX((360/m)*i deg) translateZ(z軸位移量),先圍繞x軸依次旋轉(默認旋轉點是平面中心,可以通過transform-origin設置,我們就是用默認值)(360/m)*0、 (360/m)*1 、(360/m)*2 、... 、直到(360/m)*(m-1),這樣形成的下面的效果,圖五:
圖五
咦?不是多邊體么?別急,接下里translateZ(z軸位移量)登場了,這個值是多少自己拿小本本算去... 反正是每個邊到中心點的垂直距離... ok, 看一下每個邊平移后的效果吧,圖六:
圖六
怎么超前的幾個面變這么大了?都超出粉色的框框了... 沒錯,這就是平移translateZ產生的效果,立體地看,就是前面的幾個面都跑到電腦屏幕外面了,后面的幾個面跑到電腦屏幕后面了。還不明白?就是說電腦屏幕沿着這個多邊形的中點,並且從多邊形的垂直方向穿透過去了。還不明白的話我也沒轍了...閉眼想像去吧...
(tips: 以面對電腦屏幕的視角看,假設一個平面在屏幕上,則向右為其x軸正向,向下為其y軸正向,向屏幕外為其z軸正向。且當這個平面旋轉時,他的3個軸也跟着旋轉。)
那怎么讓最外面的圖片放到電腦屏幕的平面上呢?聰明的人都知道了,把li代表的多邊形整體translateZ(z軸位移量)不就行啦!來看下效果圖,往上看,再往上看,再往上看,對了,就是圖四的效果了...
接下來給ul整體設置overflow:hidden;隱藏多出粉色邊框的部分,就看到圖一的效果了~
下方輪播圖切換點的控制:
這個就很簡單了,有m張圖,就放m個點。點擊第一個點(也是默認情況),將每個li繞x軸旋轉(360/m)*0度;點擊第二個點,將每個li繞x軸旋轉(360/m)*1度;... 直到點擊第m個點,將每個li繞x軸旋轉(360/m)*(m-1)度。接下來,我們再給每個li多邊體加上過渡transition:1s;transition-delay:
swiper-wrapper’的藍色邊框元素
完整代碼如下:
HTML:
1 <div class="swiper-wrapper"> 2 <ul id="swiper" class="swiper"></ul> 3 <div id="dotlist" class="dots"></div> 4 </div>
CSS:

1 .swiper-wrapper{ 2 width: 600px; 3 height: 350px; 4 margin-top: 100px; 5 padding: 10px; 6 box-shadow: 0px 0px 10px 5px skyblue; 7 transform-style: preserve-3d; 8 perspective: 1000px; 9 perspective-origin: 50% 50%; 10 } 11 .swiper-wrapper ul{ 12 position: relative; 13 width: 600px; 14 height: 300px; 15 box-shadow: 0px 0px 10px 2px pink; 16 overflow: hidden; 17 /*transform:translate/rotate(0px/deg); 若加這個屬性,則3d效果消失。*/ 18 } 19 .swiper-wrapper ul li{ 20 position: absolute; 21 height: 300px; 22 transform-style: preserve-3d; 23 transform-origin:0px 50% 0px; 24 transform: rotateX(0deg); 25 transition: transform 1s; 26 } 27 .swiper-wrapper ul li div{ 28 position: absolute; 29 left: 0; 30 top: 0; 31 width: 100%; 32 height: 100%; 33 background-repeat: no-repeat; 34 } 35 .swiper-wrapper .dots{ 36 height: 46px; 37 box-shadow: 0px 0px 10px 2px pink; 38 text-align: center; 39 } 40 .dots span{ 41 display: inline-block; 42 width: 10px; 43 height: 10px; 44 margin: 10px; 45 background-color: pink; 46 border-radius: 10px; 47 transition: all .5s; 48 } 49 .dots span.selected{ 50 background-color: red; 51 }
JS:

1 <script> 2 var img_num = 6;//li下的div個數,即z平面上多邊盒的邊數 3 var cut_num = 6;//li個數,每個li代表切割的一塊 4 5 var ang = 360/img_num;//每張圖片旋轉的角度 6 var ele_swiper = document.getElementById("swiper"); 7 var ele_dotlist = document.getElementById("dotlist"); 8 var ele_li; 9 var ele_div; 10 var ele_dot; 11 var swiperWidth = ele_swiper.clientWidth;//整個swiper區域的寬度 12 var width_li = swiperWidth/cut_num;//每個li的寬度 13 var tranZ_dist = 260;//每張輪播圖rotate之后要向z軸平移的距離 14 var imgChosenId = 0;//初始顯示第幾張輪播圖 15 16 for (var i = 0; i < cut_num; i++) {//遍歷li 17 ele_li = document.createElement("li"); 18 19 for (var j = 0; j < img_num; j++) {//遍歷div 20 //動態創建圖片div 21 ele_div = document.createElement("div"); 22 ele_li.appendChild(ele_div); 23 ele_div.style.backgroundImage = "url(images/"+(j+1)+".jpg)"; 24 ele_div.style.backgroundSize = swiperWidth+"px 300px";//圖片寬度等於swiper區域寬度 25 ele_div.style.backgroundPosition = (swiperWidth/cut_num)*i*(-1) + "px 0";//圖片定位 26 ele_div.style.transform = "rotateX("+ang*j+"deg) translateZ("+tranZ_dist+"px)"; 27 } 28 29 ele_swiper.appendChild(ele_li); 30 ele_li.style.width = width_li+"px"; 31 ele_li.style.left = width_li*i+"px"; 32 (i > cut_num/2-1) && (ele_li.style.zIndex = (cut_num-i)*10); 33 ele_li.style.transform = "translateZ("+tranZ_dist*(-1)+"px) rotateX("+ang*imgChosenId+"deg)"; //設置初始li翻轉角度 34 } 35 36 //動態創建輪播點按鈕 37 for (var j = 0; j < img_num; j++) { 38 ele_dot = document.createElement("span"); 39 ele_dot.setAttribute("idx",j); 40 ele_dotlist.appendChild(ele_dot); 41 if(j == imgChosenId){ 42 ele_dot.className = "selected"; 43 } 44 } 45 46 //點擊切換輪播圖 47 ele_dotlist.onclick = function doSwiper (e) { 48 var targetNode = e.target; 49 var idx; 50 var swipeNode; 51 if(targetNode.nodeName === "SPAN"){ 52 for (var ii = 0; ii < this.childNodes.length; ii++) { 53 this.childNodes[ii].className = ""; 54 }; 55 targetNode.className = "selected"; 56 idx = targetNode.getAttribute("idx"); 57 58 59 for (var z = 0; z < cut_num; z++) {//遍歷li 60 61 /*for (var zi = 0; zi < img_num; zi++) {//遍歷div 62 ele_swiper.childNodes[z].childNodes[zi] 63 64 65 }*/ 66 67 swipeNode = ele_swiper.childNodes[z]; 68 swipeNode.style.transform = "translateZ("+tranZ_dist*(-1)+"px) rotateX("+ang*idx+"deg)"; 69 swipeNode.style.transitionDelay = 0.3*z+"s"; 70 } 71 72 } 73 } 74 75 </script>
不算彩蛋的彩蛋:
本例中圖片張數(div),輪播效果翻轉片數(li),初始顯示第幾張--都是定義在js變量中的,我們也可以在頁面上寫幾個input輸入控制這些值。這么簡單的事情我就不上代碼了啊哈哈哈。就這樣吧。
最后附上github源碼,需要的同學下載(彩蛋實現和代碼優化的版本后續會更新到github)。
預覽地址: https://tinatingzhang.github.io/HTML-Demos/3D%E8%BD%AE%E6%92%AD%E5%9B%BE%E7%89%B9%E6%95%88/index.html
本文完。