• 吃透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依次翻转的过渡效果啦,聪明的你又知道了,对,就是图二的效果了~

    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      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      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      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      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输入控制这些值。这么简单的事情我就不上代码了啊哈哈哈。就这样吧。

  • 相关阅读:
    51 nod 1109 01组成的N的倍数
    zoj 1530 Find The Multiple
    洛谷 P1124 文件压缩
    洛谷 P1270 “访问”美术馆(树形DP)
    洛谷 P1272 重建道路(树形DP)
    ♫【CSS】命名颜色
    【注释】
    -_-#【命名】BEM
    ☀【jQuery插件】DOM 延迟渲染
    ☀【组件】getRequest
  • 原文地址:https://www.cnblogs.com/libin-1/p/6604481.html
Copyright © 2020-2023  润新知