最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • CSS 动画实现星球环绕效果

    正文概述 掘金(阿里巴巴TXD)   2021-07-21   893

    大家好,我是97年的前端小鲜肉绿生,日常对接设计师“奇怪”的想法?。今天为大家带来一个实用的前端小技巧。在做某 H5 活动页时,设计师山田出了一个行星环绕运动 ? 的效果图 ,五颗球需要围绕倾斜的轨道进行旋转运动。JavaScript 可以画很多复杂的动画,各种星球类的实现网上有很多,那么如何用 CSS 实现这个效果呢?

    CSS 实现效果:

    CSS 动画实现星球环绕效果

    在线 Demo 链接

    再认识 CSS Transform

    在开始之前,先回顾一些 CSS Transform 的知识点~

    Transform

    坐标系

    CSS 动画实现星球环绕效果

    X 轴:屏幕左上角为原点,水平方向为 X 轴

    Y 轴:屏幕左上角为原点,垂直方向为 Y 轴

    Z 轴:屏幕左上角为原点,垂直电脑的轴为 Z 轴,可以理解为指向我们的轴

    transform 参数的执行顺序

    transform 中传入的效果是有先后执行顺序的(效果上看,可以理解为后传入先执行,但实际计算是以矩阵matrix 的方式去算的),且转换会改变坐标轴。

    如下图,skew、scale、rotate... 本质上都是用 matrix 实现的,只不过 rotate 这种形式更容易让人上手。

    CSS 动画实现星球环绕效果

    举个例子:

    transform: rotate(30deg);
    
    // matrix(cos30°,sin30°,-sin30°,cos30°,0,0);
    transform: matrix(0.866025,0.500000,-0.500000,0.866025,0,0);
    

    这两种方式的表现结果是一致的,但如果要用纯 matrix 实现 rotate,你需要手动计算各种 sin,cos 值。

    再举两个简单的例子,帮助快速理解 transform 的计算(执行)顺序:

    1. 先执行 scaleX(0.5) 把正方形变成了长方形,再执行 rotateZ(45deg) 把元素顺时针旋转 45 度,得到的是一个倾斜的长方形:
    .test_transform {
      width:100px;
      height:100px;
      background-color: #c685d9;
      transform: rotate(45deg) scaleX(0.5);
    }
    

    CSS 动画实现星球环绕效果

    1. 先执行顺时针旋转 45 度,再压缩 X 轴,得到了一个菱形:
    .test_transform_2 {
      ...
      transform: scaleX(0.5) rotate(45deg);
    }
    

    CSS 动画实现星球环绕效果

    rotate

    简单的旋转,rotate(45deg) 其实就是 rotateZ(45deg)。

    CSS 动画实现星球环绕效果

    scale

    缩放,如 scaleY(0.6)

    CSS 动画实现星球环绕效果

    下面介绍如何用 CSS 实现一个简单的单球环绕效果:

    实现单球环绕效果

    Step1 - 基础样式

    CSS 动画实现星球环绕效果

    <div className='wrap'>
      <div className='planet'>
        <div className='ball' />
      </div>
    </div>
    
    .wrap {
      display: flex;
      background-image: linear-gradient(180deg, #020205 0%, #170f39 51%, #35247a 95%);
      width: 600px;
      height: 600px;
      align-items: center;
      justify-content: center;
    }
    
    .planet {
      position: absolute;
      border: 2px solid #fff;
      transform-style: preserve-3d;
      width: 200px;
      height: 200px;
    }
    
    .ball {
      width: 50px;
      height: 50px;
      position: absolute;
      border-radius: 50%;
      background-color: yellowgreen;
    }
    

    Step2 - 让圆形穿过轨道

    CSS 动画实现星球环绕效果

    .ball {
      // ...
      left: calc(50% - 25px);
      top: -25px;
    }
    

    为什么需要这一步?假使给这个方形加上 border-radius: 50% 转为一个圆形,目前图中所处的点才是串在圆形轨道上的,正方形四个角的点对应的半径会大于圆形的半径。

    Step3 - 旋转轨道

    CSS 动画实现星球环绕效果

    .planet {
      transform: rotateZ(45deg);
    }
    
    .ball {
      transform: rotateZ(-45deg); // 中和轨道的旋转
    }
    

    Step4 - 压缩轨道 Y 轴,形成 3D 效果

    注意先后顺序,需要先旋转再压缩,否则会变成一个倾斜的长方形

    CSS 动画实现星球环绕效果

    .planet {
      transform: scaleY(0.5) rotateZ(45deg);
    }
    
    .ball {
      // 中和轨道的 scaleY 压缩,2 * 0.5 = 1 恢复原状,注意传入顺序,和 .planet 的 transform 是相反的,就像连续上了几个不同的锁,打开时要用和上锁相反的顺序去解
      transform: rotateZ(-45deg) scaleY(2);
    }
    

    Step5 - 把轨道变成椭圆形

    CSS 动画实现星球环绕效果

    .planet {
      border-radius: 50%;
    }
    

    Step6 - 让轨道转起来

    上面的步骤已经把原来的图形变成了一个类似轨道和星球的图形了,只要遵循上述关于 rotateZ 和 scaleY 的中和规律,就能让轨道转起来,且保持球体的样式不被压缩:

    CSS 动画实现星球环绕效果

    // 公转动画
    @keyframes planet-rotate {
      0% {
        transform:  scaleY(0.5) rotate(0);
      }
      100% {
        transform:  scaleY(0.5) rotate(360deg);
      }
    }
    
    // 自转动画
    @keyframes self-rotate {
      0% {
        transform: rotate(0) scaleY(2);
      }
      100% {
        transform: rotate(-360deg) scaleY(2);
      }
    }
    
    .planet {
      animation: planet-rotate 20s linear infinite;
    }
    
    .ball {
      animation: self-rotate 20s linear infinite;
    }
    

    Step7 - 让轨道产生倾斜角度

    依旧利用 transform 的执行顺序,只要在最后再执行一个 rotate(Z),就能让整个平面产生倾斜感

    CSS 动画实现星球环绕效果

    @keyframes planet-rotate {
      0% {
        transform:  rotate(45deg) scaleY(0.5) rotate(0);
      }
      100% {
        transform:  rotate(45deg) scaleY(0.5) rotate(360deg);
      }
    }
    
    @keyframes self-rotate {
      0% {
        transform: rotate(0) scaleY(2) rotate(-45deg);
      }
      100% {
        transform: rotate(-360deg) scaleY(2) rotate(-45deg);
      }
    }
    

    实现多球环绕效果

    因为一个轨道容器最多只能保证四个球是在圆形轨道上运动的,如果要实现大于 4 个球的运动,其实只要重叠多个轨道 + 球的平面,但只展示一个轨道(border)即可。

    运动模型

    独立运动个体 = 单球体 + 单球体所在轨道(父元素)
    多球环绕 = 独立运动个体 * N 重叠在同一位置,并仅展示最底层球体所在轨道,其余轨道隐藏,最后对每个独立运动个体进行初始旋转位置的偏移

    实现步骤

    下面以 5 个球的场景为例,介绍如何实现多球环绕的效果,为了编写方便使用了 React + Sass:

    1. 编写基本 DOM 结构与样式

    Jsx

    // 传入数据
    const dataSource = [
      {
        name: '山田',
      },
      // ...
    ];
    
    // 渲染一个球 + 名字的 DOM
    const renderCircleBoxItem = (name: string) => {
      return (
        <div className={styles.circleBoxItem}>
          <div className={styles.ball} />
          <div className={styles.name}>{name}</div>
        </div>
      );
    };
    
    // 根据 dataSource 的数量来渲染多个旋转体
    <div className={styles.circleBoxWrap}>
      {
        dataSource.map((item, key) => (
          <div key={key} className={styles.circleBox}>{renderCircleBoxItem(item.name)}</div>
        ))
      }
    </div>
    

    Sass

    // 轨道层 keyframes
    @function getPlanetRotate($rotateValue) {
      @return rotate(45deg) scaleY(0.5) rotate(#{$rotateValue});
    }
    
    @keyframes planet-rotate {
      0% {
        transform: getPlanetRotate(0deg);
      }
      100% {
        transform: getPlanetRotate(360deg);
      }
    }
    
    // 自转球体 keyframes
    @function getSelfRotate($rotateValue) {
      @return rotate(#{$rotateValue}) scaleY(2) rotate(-45deg) scale(1)) translateX(50px);
      // 这里 translateX 是为了修正球的位置,使之尽量保持在轨道上运动
    }
    
    @keyframes self-rotate {
      0% {
        transform: getSelfRotate(0deg);
      }
      100% {
        transform: getSelfRotate(-360deg);
      }
    }
    
    // 轨道元素,内含一个球体
    .circleBox {
      // 统一转动速度
      $planet-rotate-speed: 30s;
      
      // 随便定个轨道大小
      width: 648px;
      height: 648px;
      position: absolute;
      transform-style: preserve-3d;
      	
      // 让轨道呈圆形
      border-radius: 50%;
    
      // 球体元素(球 + 文字 label)
      .circleBoxItem {
        position: absolute;
        display: flex;
        flex-direction: row;
        align-items: center;
          
        // 位置修正,要让球 + 文字的单元处于父元素(不加 border-radius 时是一个正方形)的一边的中心位置
        //,这样才能在形成椭圆轨道时,始终贴合轨道运动
        width: 200px; // 球 + 文字定宽,方便计算位置修正
        top: -30px; // 球的直径是 60px,向上偏移一半
        left: calc(50% - #{p2r(100)}); // 横向居中
          
        .ball {
          width: 60px;
          height: 60px;
          border-radius: 50%;
          overflow: hidden;
          border: 6px solid #fff;
          background-color: #6d45ca;
          margin-right: p2r(20);
        }
      
        .name {
          // 文字相关的样式...
        }
      }
    
      &:nth-child(1) {
        border: p2r(2) solid #fff;
        
        animation: planet-rotate $planet-ratate-speed linear infinite;
        .circleBoxItem {
          animation: self-rotate $planet-ratate-speed linear infinite;
        }
      }
    }
    

    2. 处理轨道偏移

    为了让球体们产生偏移,需要对每个 独立运动个体 的初始旋转位置产生偏移计算,对轨道的 keyframes 进行改写:

    Sass

    // 使用 css 变量控制步长(即偏移距离)
    :root {
      --planet-rotate-step: 72deg; // 72 = 360 / 5
    }
    
    @function getPlanetRotate($rotateValue) {
      @return rotate(45deg) scaleY(0.5) rotate(#{$rotateValue});
    }
    
    @keyframes planet-rotate-1 {
      0% {
        transform: getPlanetRotate(0deg);
      }
      100% {
        transform: getPlanetRotate(360deg);
      }
    }
    
    @keyframes planet-rotate-2 {
      0% {
        transform: getPlanetRotate(calc(0deg + var(--planet-rotate-step) * 1));
      }
      100% {
        transform: getPlanetRotate(calc(360deg + var(--planet-rotate-step) * 1));
      }
    }
    
    @keyframes planet-rotate-3 {
      0% {
        transform: getPlanetRotate(calc(0deg + var(--planet-rotate-step) * 2));
      }
      100% {
        transform: getPlanetRotate(calc(360deg + var(--planet-rotate-step) * 2));
      }
    }
    
    // ...
    

    3. 处理球体运动

    球体需要针对轨道的旋转路径进行位置修正,编写球体的 keyframes:

    @function getSelfRotate($rotateValue) {
      @return rotate(#{$rotateValue}) scaleY(2) rotate(-45deg) scale(1) translateX(50px);
    }
    
    @keyframes self-rotate-1 {
      0% {
        transform: getSelfRotate(0deg);
      }
      100% {
        transform: getSelfRotate(-360deg);
      }
    }
    
    @keyframes self-rotate-2 {
      0% {
        transform: getSelfRotate(calc(0deg - var(--planet-rotate-step) * 1));
      }
      100% {
        transform: getSelfRotate(calc(-360deg - var(--planet-rotate-step) * 1));
      }
    }
    
    @keyframes self-rotate-3 {
      0% {
        transform: getSelfRotate(calc(0deg - var(--planet-rotate-step) * 2));
      }
      100% {
        transform: getSelfRotate(calc(-360deg - var(--planet-rotate-step) * 2));
      }
    }
    
    // ...
    
    

    4. Animations 语句编写

    调整元素的 animation:

    .circleBox {
      &:nth-child(1) {
        // 仅显示第一个轨道
        border: p2r(2) solid #fff;
    
        animation: planet-rotate-1 $planet-rotate-speed linear infinite;
        .circleBoxItem {
          animation: self-rotate-1 $planet-rotate-speed linear infinite;
        }
      }
    
      &:nth-child(2) {
        animation: planet-rotate-2 $planet-rotate-speed linear infinite;
        .circleBoxItem {
          animation: self-rotate-2 $planet-rotate-speed linear infinite;
        }
      }
    
      &:nth-child(3) {
        animation: planet-rotate-3 $planet-rotate-speed linear infinite;
        .circleBoxItem {
          animation: self-rotate-3 $planet-rotate-speed linear infinite;
        }
      }
      
      // ...
    }
    

    5. 搭配 CSS 变量自动计算球的间距

    真实场景需要根据传入的数据个数自动处理球的间距(偏移距离),这时可以用 JS 动态计算并修改刚才的 CSS 变量来完美解决:

    const number = dataSource.length;
    const step = 360 / number;
    
    document.documentElement.style.setProperty('--planet-rotate-step', `${step}deg`);
    

    CSS 动画实现星球环绕效果

    一个星球环绕的 CSS 动画就完成啦,感谢大家观看,我们下期再会~


    2021年校招(面向2022年毕业的同学们)正式开始啦!联系我们请注明来源掘金,看下图? 了解更多信息~

    CSS 动画实现星球环绕效果


    起源地下载网 » CSS 动画实现星球环绕效果

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元