基于CSS3的3D旋转效果

  自从有了html5和css3,好多以前只能想想的华丽效果都可以上手实现了。3D 转换(个人认为3D变换更贴切^)就是其中之一。关于3D转换,可以阅读CSS3 3D transform变换,不过如此,文中对3D转换进行了形象、生动、详细的阐述。在这里,只和大家讨论怎么利用3D转换来实现立体及其旋转效果,例如:

好吧,废话不多说,上代码!

1.页面代码

 1 <div class="translate3D_test">    
 2     <ul class="cube">
 3         <li class="translate3D_li">1</li>
 4         <li class="translate3D_li">2</li>
 5         <li class="translate3D_li">3</li>
 6         <li class="translate3D_li">4</li>
 7         <li class="translate3D_li">5</li>
 8         <li class="translate3D_li">6</li>
 9     </ul>
10     <br/>
11     <a href="#" onclick="return clicka(11)"></a>
12     <a href="#" onclick="return clicka(12)"></a>
13     <a href="#" onclick="return clicka(13)"></a>
14     <a href="#" onclick="return clicka(14)"></a>
15     <a href="#" onclick="return clicka(1)">1</a>
16     <a href="#" onclick="return clicka(2)">2</a>
17     <a href="#" onclick="return clicka(3)">3</a>
18     <a href="#" onclick="return clicka(4)">4</a>
19     <a href="#" onclick="return clicka(5)">5</a>
20     <a href="#" onclick="return clicka(6)">6</a>
21     <a href="#" onclick="return clicka(0)">复原</a>
22 </div>

2.CSS代码

 1 ul
 2 {
 3     list-style-type:none;
 4     margin:0;
 5     padding:0;
 6 }
 7 li
 8 {
 9     border:0;
10 }
11 .translate3D_test{
12     position: relative;
13     width:500px;
14     height: 500px;
15     margin:auto;
16     background:#ddd;
17 }    
18 
19 .translate3D_li{
20     background:#999;
21     font-size:50px;
22     border:1px solid #F15000;
23 }

3.JS代码

  1 $(document).ready(function(){
  2         $(".cube").translate3D(400,0,0.5);
  3         $("#notcube").translate3D(400,500,0.5);
  4     });
  5     
  6     function clicka(who){            
  7         switch(who)
  8         {
  9             case 0:
 10                 $(".cube").transform3D(0);
 11                 break;
 12             case 1:
 13                 $(".cube").transform3D(1);
 14                 break;
 15             case 2:
 16                 $(".cube").transform3D(2);
 17                  break;
 18             case 3:
 19                 $(".cube").transform3D(3);
 20                 break;
 21             case 4:
 22                 $(".cube").transform3D(4);
 23                 break;
 24             case 5:
 25                 $(".cube").transform3D(5);
 26                 break;
 27             case 6:
 28                 $(".cube").transform3D(6);
 29                 break;
 30             case 11: // left
 31                 $(".cube").transform3D("left");
 32                  break;
 33             case 12: // right
 34                 $(".cube").transform3D("right");
 35                 break;              
 36             case 13: // up
 37                 $(".cube").transform3D("up");
 38                  break;         
 39             case 14: // down
 40                 $(".cube").transform3D("down");
 41                 break;
 42             default:break;
 43         };
 44         return false;
 45     }
 46     
 47     function clickb(who){            
 48         switch(who)
 49         {        
 50             case 0:
 51                 $("#notcube").transform3D(0);
 52                 break;
 53             case 1:
 54                 $("#notcube").transform3D(1);
 55                 break;
 56             case 2:
 57                 $("#notcube").transform3D(2);
 58                  break;
 59             case 3:
 60                 $("#notcube").transform3D(3);
 61                 break;
 62             case 4:
 63                 $("#notcube").transform3D(4);
 64                 break;
 65             case 11: // left
 66                 $("#notcube").transform3D("left");
 67                 break;
 68             case 12: // right
 69                 $("#notcube").transform3D("right");
 70                 break;
 71             case 13: // up
 72                 $("#notcube").transform3D("up");    
 73                 break;
 74             default:break;
 75         };
 76         return false;
 77     }
 78 
 79 /* 
 80 * translate3D 1.0 
 81 * Copyright (c) 2014 BowenLuo http://www.luobo.com/ 
 82 * Date: 2014-06-04
 83 * 基于Html5和CSS3的立体旋转效果实现,支持循环旋转,支持指定面的显示
 84 * Example:  $(".cube").translate3D(400,0,0.5);//设置类为cube的元素立方体效果,400为长宽高,当第一个参数为0时为正方体效果,0.5为旋转时间,单位为s
 85 *             $("#notcube").translate3D(400,500,0.5);//设置ID为notcube的元素立方体效果,400为长宽,500为高,0.5为旋转时间,单位为s
 86 *             $(".cube").transform3D("left");//设置类为cube的立体元素向左旋转90°
 87 *             $("#notcube").transform3D(5);//设置ID为notcube的立体元素旋转后显示第5个面,即顶部界面
 88 * 注意:1.插件需jquery支持,暂时只支持Chrome ,Safari ,Firefox 浏览器;
 89 *         2.面元素和元素容器推荐使用div标签,并请确保至少有4个面元素(正方体效果支持6个面,柱体效果支持4个面);
 90 *        3.当不为正方体效果或面元素少于6个时,请不要使用上下旋转和显示第5、6个面,以确保良好的用户体验;
 91 *         4.同一个页面可以设置多个标签的立体效果,但暂时只完美支持对一个立方体进行旋转操作;
 92 *        5.在进行旋转动作之前,请设置元素的立体效果(在文档加载完成之后设置)。
 93 */ 
 94 (function($){ 
 95 $.fn.translate3D = function(width,height,time){ 
 96     isCube = false;
 97     if(height==0){
 98         isCube = true;
 99     }
100     var thisEle=$(this);
101     if(isCube){
102         $(thisEle).css({"width":width,"height":width,"overflow":"visible"});
103         $(thisEle).children().css({"position": "absolute","width":width,"height":width});
104     }else{
105         $(thisEle).css({"width":width,"height":height,"overflow":"visible"});
106         $(thisEle).children().css({"position": "absolute","width":width,"height":height});
107     }    
108     $(thisEle).css({"-webkit-transition":"-webkit-transform "+time+"s linear","-webkit-transform-style":"preserve-3d"});
109     $(thisEle).css({"-moz-transition":"-moz-transform "+time+"s linear","-moz-transform-style":"preserve-3d"});    
110     $(thisEle).children().css({"-webkit-backface-visibility":"hidden","-moz-backface-visibility":"hidden"});
111     var mcpara = width/2;
112     var translateZ1 = "translateZ("+mcpara+"px)";        
113     var translateZ2 = "rotateY(90deg) translateZ("+mcpara+"px)";        
114     var translateZ3 = "rotateY(180deg) translateZ("+mcpara+"px)";    
115     var translateZ4 = "rotateY(-90deg) translateZ("+mcpara+"px)";
116     var translateZ5,translateZ6;    
117     $(thisEle).children("*:eq(0)").css({"-webkit-transform":translateZ1});
118     $(thisEle).children("*:eq(1)").css({"-webkit-transform":translateZ2});
119     $(thisEle).children("*:eq(2)").css({"-webkit-transform":translateZ3});
120     $(thisEle).children("*:eq(3)").css({"-webkit-transform":translateZ4});        
121     
122     $(thisEle).children("*:eq(0)").css({"-moz-transform":translateZ1});
123     $(thisEle).children("*:eq(1)").css({"-moz-transform":translateZ2});
124     $(thisEle).children("*:eq(2)").css({"-moz-transform":translateZ3});
125     $(thisEle).children("*:eq(3)").css({"-moz-transform":translateZ4});            
126     if(isCube){
127         translateZ5 = "rotateX(90deg) translateZ("+mcpara+"px) " ;
128         translateZ6 = "rotateX(-90deg) translateZ("+mcpara+"px) ";
129         $(thisEle).children("*:gt(5)").css({"display":"none"});
130         $(thisEle).children("*:eq(4)").css({"-webkit-transform":translateZ5});
131         $(thisEle).children("*:eq(5)").css({"-webkit-transform":translateZ6});
132         $(thisEle).children("*:eq(4)").css({"-moz-transform":translateZ5});
133         $(thisEle).children("*:eq(5)").css({"-moz-transform":translateZ6});        
134     }else{
135         $(thisEle).children("*:gt(3)").css({"display":"none"});
136     }            
137 }; 
138 
139 $.fn.transform3D = function(direction){ 
140     if(typeof(yAngle)!=='number'){        
141         yAngle = 0;
142     }    
143     if(typeof(xAngle)!=='number'){        
144         xAngle = 0;
145     }    
146     var thisEle=$(this);
147     var basexAngle = Math.round((xAngle%360)/360)*360+(xAngle-xAngle%360);        
148     /*
149     if(Math.abs(xAngle%360)>180){
150         if(xAngle>0){
151             basexAngle=xAngle-xAngle%360+360;
152         }
153         if(xAngle<0){
154             basexAngle=xAngle-xAngle%360-360;
155         }
156     }else{
157         basexAngle=xAngle-xAngle%360;
158     }*/
159     if(typeof(direction)=='number'){
160         
161         switch(direction)
162         {            
163             case 0:
164                 xAngle = 0;
165                 yAngle = 0;
166                 break;
167             case 1:
168                 xAngle = basexAngle;
169                 if(Math.abs(yAngle%360)>180){
170                     if(yAngle>0){                    
171                         yAngle = yAngle-yAngle%360+360;
172                     }
173                     if(yAngle<0){
174                         yAngle = yAngle-yAngle%360-360;
175                     }
176                 
177                 }else{
178                     yAngle=yAngle-yAngle%360;
179                 }
180                 break;
181             case 2:
182                 xAngle = basexAngle;
183                 if(Math.abs(-yAngle%360-90)>180){
184                     if(yAngle>0){                    
185                         yAngle = yAngle-yAngle%360-90+360;
186                     }
187                     if(yAngle<0){
188                         yAngle = yAngle-yAngle%360-90-360;
189                     }
190                 
191                 }else{
192                     yAngle=yAngle-yAngle%360-90;
193                 }
194                 break;
195             case 3:
196                 xAngle = basexAngle;
197                 if(Math.abs(-yAngle%360-180)>180){
198                     if(yAngle>0){                    
199                         yAngle = yAngle-yAngle%360-180+360;
200                     }
201                     if(yAngle<0){
202                         yAngle = yAngle-yAngle%360-180-360;
203                     }
204                 
205                 }else{
206                     yAngle=yAngle-yAngle%360-180;
207                 }
208                 break;
209             case 4:
210                 xAngle = basexAngle;
211                 Math.abs(xAngle%360)/360
212                 if(Math.abs(-yAngle%360+90)>180){
213                     if(yAngle>0){                    
214                         yAngle = yAngle-yAngle%360+90+360;
215                     }
216                     if(yAngle<0){
217                         yAngle = yAngle-yAngle%360+90-360;
218                     }            
219                 }else{
220                     yAngle=yAngle-yAngle%360+90;
221                 }
222                 break;
223             case 5:
224                 xAngle = basexAngle-90;
225                 if(Math.abs(yAngle%360)>180){
226                     if(yAngle>0){                    
227                         yAngle = yAngle-yAngle%360+360;
228                     }
229                     if(yAngle<0){
230                         yAngle = yAngle-yAngle%360-360;
231                     }                
232                 }else{
233                     yAngle=yAngle-yAngle%360;
234                 }
235                 break;
236             case 6:    
237                 xAngle = basexAngle+90;
238                 if(Math.abs(yAngle%360)>180){
239                     if(yAngle>0){                    
240                         yAngle = yAngle-yAngle%360+360;
241                     }
242                     if(yAngle<0){
243                         yAngle = yAngle-yAngle%360-360;
244                     }
245                 
246                 }else{
247                     yAngle=yAngle-yAngle%360;
248                 }            
249                 break;
250             default:break;
251         };        
252     }
253     if(direction=='left'){
254         xAngle = basexAngle;
255         yAngle -= 90;
256     } 
257     if(direction=='right'){
258         xAngle = basexAngle;
259         yAngle += 90;
260     } 
261     if(direction=='up'){
262         if(xAngle%360==0){
263             xAngle += 90;
264         }else{
265             xAngle += 180;
266         }
267         if(Math.abs(yAngle%360)>180){
268             if(yAngle>0){                    
269                 yAngle = yAngle-yAngle%360+360;
270             }
271             if(yAngle<0){
272                 yAngle = yAngle-yAngle%360-360;
273             }                
274         }else{
275             yAngle=yAngle-yAngle%360;
276         }        
277     } 
278     if(direction=='down'){
279         if(xAngle%360==0){
280             xAngle -= 90;
281         }else{
282             xAngle -= 180;
283         }
284         if(Math.abs(yAngle%360)>180){
285             if(yAngle>0){                    
286                 yAngle = yAngle-yAngle%360+360;
287             }
288             if(yAngle<0){
289                 yAngle = yAngle-yAngle%360-360;
290             }                
291         }else{
292             yAngle=yAngle-yAngle%360;
293         }            
294     }     
295     $(thisEle).css("transform","rotateX("+xAngle+"deg) rotateY("+yAngle+"deg)");
296     $(thisEle).css("webkitTransform","rotateX("+xAngle+"deg) rotateY("+yAngle+"deg)");
297     $(thisEle).css("-moz-Transform","rotateX("+xAngle+"deg) rotateY("+yAngle+"deg)");        
298 }; 
299 })(jQuery);
View Code

 

   在线演示

   整个过程可以分为2步,第一步:通过对面元素<li class="translate3D_li"></li>进行3D变换来搭建立方体;第二步:通过对面元素容器<ul class="cube"></ul>的3D变换来实现3D立体旋转效果。在搭建立方体时需要注意translateZ的使用,translateZ大小即为立方体中心到该面的垂直距离:

一般为面元素宽的一半。rotateX和roteteY可以理解为位置偏移量,例如右转90°,需要在原roteteY的基础上加90,即roteteY+=90,而不是roteteY=90。要想实现立体旋转效果,还必须设置动作效果transition:transform 2s linear(各浏览器之间不同),一个是偏转量,一个是动作时间,类似于left:'250px'和animate({left:'250px'})的关系。而要实现平滑的旋转效果,例如从当前面转到右侧面,可以左转90°,也可以右转270°,当然左转90°更优美。在这里,平滑就是转换的角度总是相对最小的。为了实现平滑的旋转,死了我大量脑细胞,结果差强人意,效果上没什么问题,感觉代码和算法有点复杂,如果大家有什么好的办法,不吝指教。我简单说下我的思路:

  1.确定目标偏移量,并确保目标便宜量与当前偏移量之间的差值小于360;

  2.计算当前偏移量和目标偏移量之间的差值,如果绝对值大于180(即270),想办法变为90;

  3.综合水平和垂直偏移量进行变换。

  其中,水平和垂直方向的偏移量是分别处理,互不干扰的,处理完后再结合变换,保证在水平和垂直方向上转换的角度都达到最小。

  PS:1.上面的js部分,从多行注释开始,是我自己写的jquery插件,为了节省时间我全粘贴出来了,哈哈。

  2.为了优雅性,除正方体外都只能显示水平方向的四个面而不要进行垂直方向上的转换;垂直方向上的变换只有三个状态:正常显示(0°),显示顶部(-90°),显示底部(90°),因为转换为180°时,会发现显示的东西都是倒立的(可以在z轴上变换180°来正常显示,但比较麻烦^)。所以在垂直方向上的平滑处理相对更简单点,可以先找到最近的正常显示的偏移量,再进行三个态的变换处理。当然,不管是垂直还是水平变换,都要先确保另一个方向上正常显示,才能保证变换后的显示是正常的。

  3.为了保存便宜量,所以在插件中定义了全局变量。这样一来,为了确保友好性,一个页面上就只能对一个立方体进行旋转操作。怎样对不同的立方体保存对应的偏移量,请大家多多指教。

 

 

posted @ 2014-06-06 12:16  小小三师弟  阅读(1366)  评论(0编辑  收藏  举报