最近在codepen上看到一个特效名为polygon Sphere
(PS:直接翻译多边形球体总感觉不能描述这个特效,这里特地形象的取了一个名字:碎片旋转)
效果如下:
实现也非常简单,下面对其源代码进行一一解释:
html部分
.container
- for (var i = 0; i < 30; i++)
.circle
- for (var j = 0; j < 25; j++)
.content
这里运用了Pug来直接生成大量的html标签,有编程经验的小伙伴可能可以直接猜到效果了,(如果对后面的文字感到困惑你可以直接看下面翻译后的代码)即在class为container的div下生成30个class为circle的div,其中每个circle包含25个class为content的div。
翻译后的html
<div class="container">
<div class="circle">*25
<div class="content"></div>*30
</div>
</div>
下面我们看看css部分,因为这里用的是css的预编译器sass,所以我强烈建议您有不懂的地方可以查看sass的官方文档(不过我也会在下面尽可能的解释)
这里没有什么好解释的,相信聪明的你完全可以自己看懂
$BG: #262626; //背景色
$CIRCLE_NUM: 30; //圈的个数
html {
width: 100%;
height: 100%;
overflow: hidden;
}
body {
position: relative;
width: 100%;
height: 100%;
margin: 0;
font-family: "Cute Font", Roboto, sans-serif;
overflow: hidden;
background-color: $BG;
}
* {
&:before,
&:after {
position: absolute;
content: "";
}
}
.container {
position: relative;
width: 300px;
height: 500px;
margin: auto;
transform: scale3d(1.0, 1.0, 1.0);
}
值得一提的是下面的
* {
&:before,
&:after {
position: absolute;
content: "";
}
}
为什么要给所有盒子添加before和after伪元素?实际上codepen作者在这里偷了个懒,马上我就会解释这里before和after的作用了!
我打算对这个特效的解读采用自底向上的方法。我们先解释最小单元content元素。
.content {
position: absolute;
width: random(150) + px;
height: random(50) + px;
top: 40%;//这里是为了使元素大致处于父元素中心
left: 40%; //这里是为了使元素大致处于父元素中心
@for $content from 1 through 25 {
&:nth-child(#{$content}) {
opacity: 0.5;
clip-path: polygon(#{random(100)}% 0, #{random(100)}% 0, #{random(100)}% 48%); //画三角
}
}
首先从定义三角形碎片开始,这里利用了clip-path 随机的画了3个点,刚好能形成一个三角形。
$content是索引,索引1~25每个content(这里是三角形单元),每个三角形的形状都是随机的!
然后给这个三角形上色(请先不要看下面的transfrom)
@if $circle <= $CIRCLE_NUM / 2 {
background: hsl(245, random(100), 50);
//transform: rotate($content * 14.4deg) translate(0, #{($circle + 5 )* 10}px);
}
@else
background: hsl(180, random(100), 50);
//transform: rotate($content * 14.4deg) translate(0, #{($CIRCLE_NUM - $circle + 5) * 10}px);
}
注意这里的if是为了给上下风暴区分主色调,你可以看看最顶上的gif,是不是上下的三角形颜色层次分明?
基础三角形.png
然后,如果一个三角形只有纯色调,就太单调了!
所以这里要为内部的伪元素上色,并用clip-path完成挪动位置的效果
&:before, &:after {
width: random(100) + px;
height: random(50) + px;
clip-path: polygon(#{random(100)}% 0, #{random(100)}% 0, #{random(100)}% 48%);
}
&:before {
background: #fff;
}
&:after {
top: 10px;
left: 30px;
background: hsl(276, random(100), 50);
background: hsl(276, 100, 50);
}
上色后.png
是不是有点不知所措看不懂?没关系,我去掉了content元素的clip-path,这下真相大白了!
image.png
定义好单独的三角形元素后,接下来就是要用三角形组成一个层,这个层的表现形式实际上就是一个个分层的圈,在html也是content的上级div:circle,PS:后面出现的$circle是层(圈)的索引 image.png
如何完成这个圈的绘制?
实际上就是让所有在中心的三角形转过一个角度,然后再向y轴移动一个位移
@if $circle <= $CIRCLE_NUM / 2 {
background: hsl(245, random(100), 50);
transform: rotate($content * 14.4deg) translate(0, #{($circle + 5 )* 10}px);
}
@else {
background: hsl(180, random(100), 50);
transform: rotate($content * 14.4deg) translate(0, #{($CIRCLE_NUM - $circle + 5) * 10}px);
}
}
transfrom的y轴中的算法,让015的圈越来越大,而1530的圈变的越来越小.这样便能形成一个螺旋的效果
.circle {
position: absolute;
border-radius: 50%;
@for $circle from 1 through $CIRCLE_NUM {
&:nth-child(#{$circle}) {
width: 200px;
height: 200px;
transform: skew(0, -10deg);
top: #{$circle * 15}px; //让层之前有一定距离,这个距离是斜向的
left: #{$circle * 3.22}px;//让层之前有一定距离 ,这个距离是斜向的
//使层旋转,根据索引位置不同,产生不同的旋转方向
@if $circle % 3 == 0 {
animation: rotate-reverse #{$circle + random(10)}s linear infinite;
}
@else {
animation: rotate #{$circle + random(10) }s linear infinite;
}
.content {
...
}
使层转起来的动画
@keyframes rotate {
0% {
transform: skew(0, -10deg) rotate(0);
}
100% {
transform: skew(0, -10deg) rotate(360deg);
}
}
@keyframes rotate-reverse {
0% {
transform: skew(0, -10deg) rotate(360deg);
}
100% {
transform: skew(0, -10deg) rotate(0);
}
}
scss最终源代码:
$BG: #262626;
$CIRCLE_NUM: 30;
html {
width: 100%;
height: 100%;
overflow: hidden;
}
body {
position: relative;
width: 100%;
height: 100%;
margin: 0;
font-family: "Cute Font", Roboto, sans-serif;
overflow: hidden;
background-color: $BG;
}
* {
&:before,
&:after {
position: absolute;
content: "";
}
}
.container {
position: relative;
width: 300px;
height: 500px;
margin: auto;
transform: scale3d(1.0, 1.0, 1.0);
}
.circle {
position: absolute;
border-radius: 50%;
@for $circle from 1 through $CIRCLE_NUM {
&:nth-child(#{$circle}) {
width: 200px;
height: 200px;
transform: skew(0, -10deg);
top: #{$circle * 15}px;
left: #{$circle * 3.22}px;
@if $circle % 3 == 0 {
animation: rotate-reverse #{$circle + random(10)}s linear infinite;
}
@else {
animation: rotate #{$circle + random(10) }s linear infinite;
}
.content {
position: absolute;
width: random(150) + px;
height: random(50) + px;
top: 40%;
left: 40%;
@for $content from 1 through 25 {
&:nth-child(#{$content}) {
opacity: 0.5;
clip-path: polygon(#{random(100)}% 0, #{random(100)}% 0, #{random(100)}% 48%);
&:before, &:after {
width: random(100) + px;
height: random(50) + px;
clip-path: polygon(#{random(100)}% 0, #{random(100)}% 0, #{random(100)}% 48%);
}
&:before {
background: #fff;
}
&:after {
top: 10px;
left: 30px;
background: hsl(276, random(100), 50);
background: hsl(276, 100, 50);
}
@if $circle <= $CIRCLE_NUM / 2 {
background: hsl(245, random(100), 50);
transform: rotate($content * 14.4deg) translate(0, #{($circle + 5 )* 10}px);
}
@else {
background: hsl(180, random(100), 50);
transform: rotate($content * 14.4deg) translate(0, #{($CIRCLE_NUM - $circle + 5) * 10}px);
}
}
}
}
}
}
}
@keyframes rotate {
0% {
transform: skew(0, -10deg) rotate(0);
}
100% {
transform: skew(0, -10deg) rotate(360deg);
}
}
@keyframes rotate-reverse {
0% {
transform: skew(0, -10deg) rotate(360deg);
}
100% {
transform: skew(0, -10deg) rotate(0);
}
}
是不是很简单?赶紧自己动手实现一个~
网友评论