吃透css3之3d屬性--perspective和transform


本文為原創,轉載請注明出處: cnzt       文章:cnzt-p

 

 

  寫在前面:最近寫了個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: (0.6 * (i-1))s; 這樣就有每個li依次翻轉的過渡效果啦。這里划重點:每個li翻轉3d效果還要加上transform-style:perserve-3d; 然后聰明的你又知道了,對,就是圖二的效果了~

 

perspecitve到底什么鬼?

  說了這么多,效果都實現了,我咋還不講perspective呢?別急,這不是來了嘛... 如文章一開始所說,perspective會讓你看到一種3d立體空間感,而非二位平面的體驗了。本例中的perspective用在了class=‘swiper-wrapper’的藍色邊框元素上,賦值perspective:1000px; 它的取值越小,你在電影院的座位越靠前,視覺效果越近;反之,取值越大,你在電影院座位約靠后,視角越遠。實際應用中,800-1000px效果較好,相當於電影院四五排的座位,got it?聰明的人一定get了... 下面上對比效果圖:

 

完整代碼如下:

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 }
View Code

 

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>
View Code

 

 

不算彩蛋的彩蛋

  本例中圖片張數(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 

 

本文完。

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM