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类数组变为数组
var oLi = Array.prototype.slice.call(document.getElementsByTagName('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
现在转换为代码:
var deg = (Math.round((Math.atan2(y,x) * (180/Math.PI)+180) / 90)+3) % 4;
现在四个方向就可以用一个数字来代表
完整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; // 这里采用了类名拼接的方式
}
最后这个项目就完成了。