有手就会系列——实现日地月公转

1,263 阅读5分钟

业务开发过程中,经常用到日期格式化url参数转对象浏览器类型判断节流函数等常用函数,为避免不同项目多次复制粘贴的麻烦,我封装了工具库github.com/CatsAndMice…。如果你也有常用的代码,欢迎为本项目提交pr。

前言

打工人的无奈,公司要求每个人都要进行培训。为了完成它,上网找出好几个demo,都没有勾起我动手实现一波的欲望。日常浏览掘金时,偶然看到一篇【中秋】纯CSS实现日地月的公转,决定就是它了。

实现效果: 日地月公转 (codepen.io)

实现日地月公转

HTML5布局

布局方面非常简单,使用三个div 分别表示日,地,月,另外再加div.contian 进行包裹

<div class="contian">
    <div class="sun"></div>
    <div class="earth">
        <div class="moon"></div>
    </div>
</div>

设置全局背景

body 天生自带margin:8px; 影响布局,去除即可

html,body 均为块级元素,独占一行,但高度不会占满窗口高度。我们需要让其撑满整个窗口,设置它们的高度为height:100vh;

html,
body {
    margin: 0;
    padding: 0;
    background-color: #2f3141;
    height: 100vh;
}

定位居中

我们需要将所有内容都相对浏览器窗口居中展示,居中方式有:flex,grid 布局,定位以及设置margin 等。

这里绘图说明下如何margin居中:

给内容具体的宽高并设置margin-left:aute;margin-right:aute; 另外通过calc((100vh - 内容高度) / 2)

得到margin-top 的值。

当然使用margin居中麻烦,这里我只提供一个思路。

日地月居中,我选择使用定位完成

body 是占满窗口的,我们以body 进行绝对定位。定位前需要给body设置相对定位属性position: relative; 然后给div.contian 添加绝对定位属性position: absolute;

html,
body {
    margin: 0;
    padding: 0;
    background-color: #2f3141;
    position: relative;
    height: 100vh;
}

.contian {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 500px;
    height: 500px;
    border: 1px solid #ccc;
    transform: translate(-50%, -50%);
}

绝对定位时,top,left 值是以div.contian 左上角为准,所以当left:50%;right:50% div.contian 并没有达到居中的效果,left,right 值页面需要减div.contian自身宽高的值。

为了达到效果,我使用了 transform: translate(-50%, -50%); 让其以自身的宽高为准向X轴,Y轴,各移动-50%

设置transform 可以理解为创建了一个三维坐标如下图,不同的是Z轴是正对屏幕,面对屏幕前的您。这里对transform 不做过多文字描述

为了更直观的看到效果,我给div.contian加了一个border边框

模拟实现太阳

同理,使div.sun 相对div.contian 绝对定位居中。设置border-radius:50%绘制成圆形,backgroud使用线性渐变 linear-gradient填充;为了效果更佳,给div.sun 一定的外阴影

div.sun元素设置宽高,给外阴影 box-shadow: 0 0 8px 8px rgba(242, 120, 75, 0.2);

.sun {
    position: absolute;
    border-radius: 50%;
    height: 200px;
    width: 200px;
    left: 50%;
    top: 50%;
    background: linear-gradient(#fcd670, #f2784b);
    transform: translate(-50%, -50%);
    box-shadow: 0 0 8px 8px rgba(242, 120, 75, 0.2);
}

太阳效果如图:

模拟实现地球

地球公转轨道

地球会绕太阳进行运动,运动的轨道是一个圆形的,我们可以给div.earth 宽高设置与最外层div.contian 一致

为直观我也给div.earth添加了一个border 属性

.earth {
    width: 500px;
    height: 500px;
    border-radius: 50%;
    border:1px solid #f2784b;
}

这个圆边框将用于地球公转运动轨道

伪元素::before实现行星

行星都用一个圆进行模拟,同理使用 border-radius: 50%; 给背景渐变色。将地球定位至地球公转轨道正上文 也就是div.earth 的正上方

.earth {
  //...
  position: relative;
}
.earth::before {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 60px;
    height: 60px;
    background: linear-gradient(#19b5fe, #7befb2);
    border-radius: 50%;
}

地球效果如图:

伪元素::after实现行星尾巴

行星尾巴使用border 模拟,设置::after伪元素width:100%;heigth:100%;,占满div.earth

::after伪元素border-top:px solid silver;border-left:22px solid transparent;

div.earthborder去除,这个时候我们可以清楚的看到行星尾巴已经出来了

但行星尾巴并没有与地球交接在一起,这里可以通过设置transform中的rotateZ,让其绕Z轴逆时针旋转一定角度

此时,地球完成了,只差让其绕太阳旋转

div.earth元素添加animation属性,定义动画关键帧keyframes ,让div.earth 一直Z轴是顺时针旋转

.earth {
    width: 500px;
    height: 500px;
    border-radius: 50%;
    position: relative;
    animation: rotate 20s linear infinite;
}

@keyframes rotate {
    0% {
        transform: rotateZ(0deg);
    }

    100% {
        transform: rotateZ(360deg);
    }

}

.earth::before {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 60px;
    height: 60px;
    background: linear-gradient(#19b5fe, #7befb2);
    border-radius: 50%;
}
 .earth::after {
      position: absolute;
      content: "";
      width: 100%;
      height: 100%;
      border-top: 2px solid silver;
      border-right: 2px solid transparent;
      border-radius: 50%;
      transform: rotateZ(-52deg);
  }

添加动画属性后效果:

模拟实现月亮

<div class="earth">
    <div class="moon"></div>
</div>

html布局上,div.moondiv.earth包裹,所以我们依然通过定位,将div.moon 固定至地球公转轨道正上文 也就是div.earth 的正上方

月亮公转轨道圆应大于地球

.moon {
    position: absolute;
    left: 50%;
    top: 0;
    width: 100px;
    height: 100px;
    border-radius: 50%;
    border: 1px solid #19b5fe;
    transform: translate(-50%, -50%);
}

为直观我给div.moon 添加了border

留下的就是使用div.moon的伪元素模拟出月亮与月亮旋转的尾巴,思路与模拟实现地球相同

到此,文章不再添加文字描述思路

最后将不必要的border删除,一个有手就会的日地月公转就完成了

完整代码不贴出来了,想要源码移步日地月公转 (codepen.io)

当然,还可以扩展实现3D效果的公转自转,有兴趣的小家伙可以实现下

实现行星公转自转 (codepen.io)

总结

文章使用定位,transform,animation 等css3属性,从0到1实现了日地月公转