今天在网上看到一个国外写的CSS3 3D Bouncing Ball(3D反弹球),很有意思,并且实现起来也不难,于是忍不住自己也试写一遍。
好玩的东西当然要大家一起分享,所以接下来我就简单讲解一下如何实现跳动的效果
首先讲解一下结构,wrap是用来控制小球跳动的区域,ball就是小球啦,ballshadow是用来制作小球的投影,因为小球的运动和投影部分的运动(动画)效果是分开的,所以小球和投影部分要两个div来组成,而wrap设置的高度即为小球运动的高度,此时我们就可以通过top属性来很好的控制小球的位置变化。
小球运动区域CSS
.wrap {
width: 150px;
height: 300px;
position: absolute;
left: 50%;
top: 50%;
margin: -150px 0 0 -75px;
}
小球的CSS
通过渐变和投影来制作小球的立体感
.ball {
width: 100px;
height: 100px;
border-radius: 50%;
background: rgb(187,187,187);
background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2JiYmJiYiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9Ijk5JSIgc3RvcC1jb2xvcj0iIzc3Nzc3NyIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgPC9saW5lYXJHcmFkaWVudD4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPgo8L3N2Zz4=);
background: -moz-linear-gradient(top, rgba(187,187,187,1) 0%, rgba(119,119,119,1) 99%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(187,187,187,1)), color-stop(99%,rgba(119,119,119,1)));
background: -webkit-linear-gradient(top, rgba(187,187,187,1) 0%,rgba(119,119,119,1) 99%);
background: -o-linear-gradient(top, rgba(187,187,187,1) 0%,rgba(119,119,119,1) 99%);
background: -ms-linear-gradient(top, rgba(187,187,187,1) 0%,rgba(119,119,119,1) 99%);
background: linear-gradient(top, rgba(187,187,187,1) 0%,rgba(119,119,119,1) 99%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#bbbbbb', endColorstr='#777777',GradientType=0 );
box-shadow: inset 0 -5px 15px rgba(255,255,255,0.4),
inset -2px -1px 40px rgba(0,0,0,0.4),
0 0 1px #000;
position: absolute;
z-index: 99;
}
小球反光面的CSS
利用after伪类来实现,通过渐变和圆角属性来实现半透明椭圆形遮罩
.ball::after {
content: "";
width: 60px;
height: 30px;
position: absolute;
left: 20px;
top: 10px;
z-index: 10;
background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2U4ZThlOCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjElIiBzdG9wLWNvbG9yPSIjZThlOGU4IiBzdG9wLW9wYWNpdHk9IjEiLz4KICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwIi8+CiAgPC9saW5lYXJHcmFkaWVudD4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPgo8L3N2Zz4=);
background: -moz-linear-gradient(top, rgba(232,232,232,1) 0%, rgba(232,232,232,1) 1%, rgba(255,255,255,0) 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(232,232,232,1)), color-stop(1%,rgba(232,232,232,1)), color-stop(100%,rgba(255,255,255,0)));
background: -webkit-linear-gradient(top, rgba(232,232,232,1) 0%,rgba(232,232,232,1) 1%,rgba(255,255,255,0) 100%);
background: -o-linear-gradient(top, rgba(232,232,232,1) 0%,rgba(232,232,232,1) 1%,rgba(255,255,255,0) 100%);
background: -ms-linear-gradient(top, rgba(232,232,232,1) 0%,rgba(232,232,232,1) 1%,rgba(255,255,255,0) 100%);
background: linear-gradient(top, rgba(232,232,232,1) 0%,rgba(232,232,232,1) 1%,rgba(255,255,255,0) 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#e8e8e8', endColorstr='#00ffffff',GradientType=0 );
border-radius: 40px / 20px;
}
投影部分的CSS
利用半透明的背景和模糊滤镜来制作投影效果
.ballshadow {
width: 100px;
height: 50px;
border-radius: 50%;
background: rgba(0,0,0,0.3);
position: absolute;
filter: url(blur.svg#blur); /* FireFox, Chrome, Opera */
-webkit-filter: blur(10px); /* Chrome, Opera */
-moz-filter: blur(10px);
-ms-filter: blur(10px);
filter: blur(10px);
filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius=10, MakeShadow=false); /* IE6~IE9 */
bottom: 0;
}
这样就完成了整个画布的布局,接下来我们要为他增加动画效果,首先是小球的运动
这里只贴出一个兼容-webkit-的写法,通过scale(缩放)来使当小球产生一种反弹挤压的效果,原作中是通过控制border-radius的变化来产生挤压效果,感觉不太好计算,倒不如缩放来的爽快。
同时有一点要注意,当小球的top等于wrap的高度减去小球自身的高度时就是小球碰撞到底部的位置了。
@-webkit-keyframes bounce {
0% {top: 0;
-webkit-animation-timing-function: ease-in;
}
40% {}
50% {
top: 180px;
-webkit-animation-timing-function: ease-out;
-webkit-transform:scale(1);
}
55% {
top: 200px;
-webkit-transform:scale(1, 0.65);
-webkit-animation-timing-function: ease-in;
}
65% {
top: 160px;
-webkit-transform:scale(1);
-webkit-animation-timing-function: ease-out;}
95% {
top: 0;
-webkit-animation-timing-function: ease-in;
}
100% {
top: 0;
-webkit-animation-timing-function: ease-in;
}
}
投影部分的动画:
投影部分的动画再简单不过了,原作中同样是使用border-radius来控制一个缩放的效果,这里我们直接使用scale,并且配合动画的速度曲线属性来迎合跳动小球变化
@-webkit-keyframes shrink {
0% {
-webkit-transform:scale(1);
-webkit-animation-timing-function: ease-in;
}
50% {
-webkit-transform:scale(0.7);
-webkit-animation-timing-function: ease-out;
}
100% {
-webkit-transform:(1);
-webkit-animation-timing-function: ease-in;
}
}
接着我们为元素添加上相应的动画就OK了
.ball {
animation: bounce 1s infinite linear;
-webkit-animation: bounce 1s infinite linear;
-moz-animation: bounce 1s infinite linear;
-o-animation: bounce 1s infinite linear;
}
.ballshadow {
-webkit-animation: shrink 1s infinite linear;
-moz-animation: shrink 1s infinite linear;
animation: shrink 1s infinite linear;
-o-animation: shrink 1s infinite linear;
}
那么该如何实现点击hold住的时候让小球越跳越远?其实这只是利用了一个视觉效果,让整个wrap缩放,由1至0就会产生这样的效果,这里的实现方式就不说了,有兴趣的话大家自己尝试一下,利用active伪类就可以实现按下hold出现的状态。