3D翻轉盒子圖檔展示特效
首先展示一下完成的效果,這個demo最難的部分在于滑鼠進入不同的邊,要自動判斷方向,這個問題在後面會有詳細的解決辦法!

主要程式設計思想:樣式與行為相分割
第一步:建構html結構
在一個容器中,生成六個小盒子,這裡我使用了 ul li 的方式 在每一個li中 有一個 pic-area 用來包裹圖檔和文字,我這裡使用了彈性盒子布局的方式,實作了示範的布局,其他方式也都可以,然後通過把文字區域定位,與圖檔區重疊,為後續建構立方體做準備,為了看得清楚,我增加了一點點透明度,後期會删掉
一部分html代碼如下:
<div class="wrapper">
<ul>
<li>
<div class="pic-area">
<img src="./tq.jpg" alt="">
<p>Lorem, ipsum dolor.</p>
</div>
</li>
<!-- 此處是六個li 其他的省略不寫了 -->
</ul>
</div>
以下是css代碼,僅供參考:
*{
margin: 0;
padding: 0;
list-style: none;
}
.wrapper{
width: 1000px;
height: 650px;
border: 1px solid #424242;
margin: 20px auto;
}
ul{
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
ul>li{
width: 300px;
height: 300px;
margin: 10px;
}
.pic-area{
position: relative;
transform-style: preserve-3d;
}
.pic-area img{
opacity: 0.4;
}
.pic-area p{
position: absolute;
top: 0;
left: 0;
width: 300px;
height: 300px;
background-color: #424242;
font-size: 30px;
line-height: 300px;
text-align: center;
transform: translate3d(0 ,0 ,-1px);
}
第二步:如何實作立方體
首先要注意的是:雖然看起來是一個六面立方體,但是我隻用了兩個面,我定義了四個方向的類名,滑鼠在不同的方向移入,我添加對應的類名即可
我們要翻轉的是圖檔後面的文字部分,注意文字部分在翻轉90度的時候,他翻轉的中心是在中心的位置,如下圖所示,翻轉之後還要往上,往裡平移
但是我采用了一種簡單的方法,首先平移,然後改變翻轉源點,最後直接翻轉
css代碼如下:
.in-top .pic-area .hide{
transform-origin: bottom;
transform: translate3d(0,-100%,0) rotate3d(1,0,0,90deg);
}
值得注意的是 不要忘記如下代碼,一個是景深屬性 ,一個是開啟3D模式
現在應該有一個這樣的效果
同理:四個面四個類名
.in-top .pic-area .hide{
transform-origin: bottom;
transform: translate3d(0,-100%,0) rotate3d(1,0,0,90deg);
}
.in-bottom .pic-area .hide{
transform-origin: top;
transform: translate3d(0,100%,0) rotate3d(-1,0,0,90deg);
}
.in-left .pic-area .hide{
transform-origin: right;
transform: translate3d(-100%,0,0) rotate3d(0,1,0,-90deg);
}
.in-right .pic-area .hide{
transform-origin: left;
transform: translate3d(100%,0,0) rotate3d(0,1,0,90deg);
}
設定在不同的li上看一下現在的效果 如圖所示:
接下來這是轉動效果,這裡主要通過css3的animate效果來實作
首先改變圖檔區的轉動源,不然會有一個突出去的效果 ,150px是盒子寬高的一半,讓盒子的轉動軸固定在盒子中心
.pic-area{
position: relative;
transform-style: preserve-3d;
transform-origin: 50% 50% -150px;
animation: 0.3s ease-out forwards;
}
緊接着,定義四個關鍵幀 ,然後給不同的方向添加動畫 animation除了name是公有屬性,是以提出來在pic-area中一起設定了,下面是分别設定動畫名字
.wrapper .in-top .pic-area{
animation-name: in-top;
}
.wrapper .in-bottom .pic-area{
animation-name: in-bottom;
}
.wrapper .in-left .pic-area{
animation-name: in-left;
}
.wrapper .in-right .pic-area{
animation-name: in-right;
}
@keyframes in-top{
0%{transform: rotate3d(0,0,0,0)}
100%{transform: rotate3d(-1,0,0,90deg)}
}
@keyframes in-bottom{
0%{transform: rotate3d(0,0,0,0)}
100%{transform: rotate3d(1,0,0,90deg)}
}
@keyframes in-left{
0%{transform: rotate3d(0,0,0,0)}
100%{transform: rotate3d(0,1,0,90deg)}
}
@keyframes in-right{
0%{transform: rotate3d(0,0,0,0)}
100%{transform: rotate3d(0,-1,0,90deg)}
}
這是轉動之後的效果,這裡我也給文字區加了一個透明度
上面呢是進入的動畫,接下來寫出去的動畫
道理其實很簡單,就是把順序換一下就好了
@keyframes out-top{
0%{transform: rotate3d(-1,0,0,90deg)}
100%{transform: rotate3d(0,0,0,0)}
}
@keyframes out-bottom{
0%{transform: rotate3d(1,0,0,90deg)}
100%{transform: rotate3d(0,0,0,0)}
}
@keyframes out-left{
0%{transform: rotate3d(0,1,0,90deg)}
100%{transform: rotate3d(0,0,0,0)}
}
@keyframes out-right{
0%{transform: rotate3d(0,-1,0,90deg)}
100%{transform: rotate3d(0,0,0,0)}
}
添加出去的動畫名稱
.wrapper .out-top .pic-area{
animation-name: out-top;
}
.wrapper .out-bottom .pic-area{
animation-name: out-bottom;
}
.wrapper .out-left .pic-area{
animation-name: out-left;
}
.wrapper .out-right .pic-area{
animation-name: out-right;
}
添加出去的動畫效果,這裡我就和進入的動畫效果放在了一起寫,這樣可以減少代碼量
.out-top .pic-area .hide,
.in-top .pic-area .hide{
transform-origin: bottom;
transform: translate3d(0,-100%,0) rotate3d(1,0,0,90deg);
}
.out-bottom .pic-area .hide,
.in-bottom .pic-area .hide{
transform-origin: top;
transform: translate3d(0,100%,0) rotate3d(-1,0,0,90deg);
}
.out-left .pic-area .hide,
.in-left .pic-area .hide{
transform-origin: right;
transform: translate3d(-100%,0,0) rotate3d(0,1,0,-90deg);
}
.out-right .pic-area .hide,
.in-right .pic-area .hide{
transform-origin: left;
transform: translate3d(100%,0,0) rotate3d(0,1,0,90deg);
}
四個方向樣式的設定原理相同,隻要清楚一個方向的樣式設定方法就可以。
每一個方向都有一組相應的動畫,通過類名來控制,分别是 in-方向 與 out-方向
第三步:也是最重要的一步:如何判斷從那個方向進入的呢?
首先:擷取li對象,并把li類數組變為數組
給每一個li綁定事件
oLi.forEach(function(ele ,index){
ele.addEventListener('mouseenter',function(e){
addClass(this,e);
})
})
定義一個增加類名的函數
function addClass(ele,e) {
// 判斷從那個方向進入
// 首先擷取滑鼠進入的位置
var x = e.offsetX - ele.offsetWidth / 2;
var y = e.offsetY - ele.offsetHeight / 2;
var deg = Math.atan2(y,x) * (180/Math.PI); //角度值與弧度值的轉換
}
這是滑鼠進入一個盒子的時候會産生一個弧度值,這裡我把offsetWidth/2是為了把坐标圓心移動到中間,這樣就會得到一周的角度,然後再通過一個數學公式,轉化成角度,這就是滑鼠進入時的不同角度
但是這個樣子角度的區間會亂,有正有負,是以我給所有的角度都加了180度,這樣是以的角度就都變成了一個正數,而且是從0度到360度
我們不妨來看一下此時各個方向的角度範圍
- top :45~135
- right:135~225
- bottom:225~315
- left:0~45 & 315~360
但是現在這個度數看起來還是不友善我們操作,是以繼續化簡,給每一個都除以90°
那麼現在的角度範圍是:
- top :0.5~1.5
- right :1.5~2.5
- bottom :2.5~3.5
- left :0~0.5 & 3.5~4
繼續化簡,進行四舍五入取整
- top :1
- right:2
- bottom:3
-
left:0 & 4
但是現在left中有兩個數字代表了一個方向,是以要繼續進行處理 ,每一個都+3,之後變為 4,5, 6,3&7 ,然後%4取餘4
- top:0
- right:1
- bottom :2
-
left:3
現在轉換為代碼:
現在四個方向就可以用一個數字來代表
完整js代碼如下:
var oLi = Array.prototype.slice.call(document.getElementsByTagName('li'));
//擷取li對象,并把擷取到li類數組轉換為數組
oLi.forEach(function(ele ,index){
ele.addEventListener('mouseenter',function(e){
addClass(this,e,'in');
})
ele.addEventListener('mouseleave',function(e){
addClass(this,e,'out');
})
})
function addClass(ele,e,state) {
// 判斷從那個方向進入
// 首先擷取滑鼠進入的位置
var x = e.offsetX - ele.offsetWidth / 2;
var y = e.offsetY - ele.offsetHeight / 2;
var deg = (Math.round((Math.atan2(y,x) * (180/Math.PI)+180) / 90)+3) % 4;
var direction;
switch(deg){
case 0 :
direction = 'top';
break;
case 1 :
direction = 'right';
break;
case 2 :
direction = 'bottom';
break;
case 3 :
direction = 'left';
break;
}
ele.className = state + '-' + direction; // 這裡采用了類名拼接的方式
}
最後這個項目就完成了。