探索!单标签实现酷炫的css动画

背景

在上一篇文章一个炫酷的css动画里面,我们实现了一个酷炫的css动画。

那个最终实现上面,用了很多的标签在里面,有一些是效果需要,但是有一些是可以精简的。

于是想减少一下标签的使用来实现类似的效果,既然减少了,不如干脆一点,我们就用一个标签来尝试实现类似的效果。(不一完全一样的想效果,有些效果一个标签不好实现,下文会详细提到)

这是我们最终用单标签将要实现的效果。

老规矩,先分析一下效果,一共有六个部分,最外层圈,第二层圈,下面的一对圆环,上面的一对圆环,还有类流星雨动画的元素。

先把唯一的一个元素放好。

<div class="box"></div>

好,就这一个元素,html部分结束,下面全是css部分了!!!

最外层圈

这里第一反应应该是border,但是其实还有outline可以用,这里我们把border留给第二层用,我们第一层用outline来实现。

js 复制代码
.box {
        width: 300px;
        height: 300px;
        border-radius: 50%;
        outline: 2px solid rgb(42, 153, 255);
        outline-offset: 10px;
      }

得到外面的圆环

第二层圈

第二层我们用border, 然后把上下或者左右的边设为透明

box的样式上加上这些代码

js 复制代码
{
 border: 5px solid rgb(42, 153, 255);
        border-left-color: transparent;
        border-right-color: transparent;
}

下面的一堆圆环

上一篇文章里面,我们用了两种方式实现这个圆环

一种是conic-gradientmask,先利用角向渐变生成扇形,然后用mask遮住下面部分,得到圆环,然后重复多个,给每个不同的旋转角度。

第二种是repeating-conic-gradientmask,就是一次性生成带黑色间隔的扇形,然后mask遮住。

这里面我们还有第三种实现方式:

repeating-radial-gradientrepeating-conic-gradient

重复的径向渐变加重复的角向渐变来实现。

径向渐变可以得到一个完整的圆环,角向渐变可以得到黑色间隙的一堆小圆环。 这里面我们给box加上background

js 复制代码
 background: repeating-radial-gradient(
            #000,
            #000 50%,
            transparent 50%,
            transparent 60%,
            #000 60%,
            #000 100%
          ),
          repeating-conic-gradient(
            rgb(42, 153, 255) 0,
            rgb(42, 153, 255) 4%,
            transparent 4%,
            transparent 5%
          );

上面的一堆圆环

上面的一堆圆环实现起来其实和下面的一样,只是我们不能再用box的backgorund来实现了,因为有一个3d效果,需要沿着z轴做位移。

那我们其实还有两个工具没有用,那就是伪元素。这里我们先用一个::before

js 复制代码
.box::before {
        content: '';
        position: absolute;
        width: 300px;
        height: 300px;
        top: 0px;
        left: 0px;
        background: repeating-radial-gradient(
            #000,
            #000 50%,
            transparent 50%,
            transparent 60%,
            #000 60%,
            #000 100%
          ),
          repeating-conic-gradient(
            rgb(42, 153, 255) 0,
            rgb(42, 153, 255) 4%,
            transparent 4%,
            transparent 5%
          );
        transform: translateZ(200px);
      }

类流星雨元素

还剩下一个元素,我们就可以用另一个伪元素了,::after。 上一篇文章里面,我们用了一些元素来实现流星雨的动画,每一个元素代表其中一颗。这里我们只用一个伪元素实现的话,第一反应应该是用box-shadow来实现。

用box-shadow来实现对自身的多次复制,不过这里有一个缺陷,就是box-shadow默认是黑色,可以指定颜色但是不支持渐变。所以用box-shadow行不通,我们换一种方式来实现。

这里我们还是用repeating-linear-gradientmask来实现的。

重复的线性渐变实现几个自上而下的渐变色

然后我们再用mask,获得色条

js 复制代码
.box::after {
        content: '';
        position: absolute;
        width: 300px;
        height: 300px;
        top: 0px;
        left: 0px;
        background: #fff;
        background: repeating-linear-gradient(
          to bottom,
          transparent 0,
          transparent 80px,
          rgba(42, 153, 255, 0.1) 80px,
          rgb(42, 153, 255) 130px
        );
        -webkit-mask: repeating-linear-gradient(
          to right,
          transparent 0px,
          transparent 80px,
          #000 80px,
          #000 82px
        );
        transform: rotateX(90deg);
        animation: line 2s linear infinite;
      }

到这里,我们就完整的用单标签实现了这样的一个动画效果。

全部代码如下:

js 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      body {
        background: #000;
        display: flex;
        align-items: center;
        justify-content: center;
        padding-top: 300px;
        transform: translateZ(-150px);
        transform-style: preserve-3d;
      }
      .box {
        width: 300px;
        height: 300px;
        transform-style: preserve-3d;
        position: relative;
        border-radius: 50%;
        border: 5px solid rgb(42, 153, 255);
        border-left-color: transparent;
        border-right-color: transparent;
        outline: 2px solid rgb(42, 153, 255);
        outline-offset: 10px;
        background: repeating-radial-gradient(
            #000,
            #000 50%,
            transparent 50%,
            transparent 60%,
            #000 60%,
            #000 100%
          ),
          repeating-conic-gradient(
            rgb(42, 153, 255) 0,
            rgb(42, 153, 255) 4%,
            transparent 4%,
            transparent 5%
          );
        animation: rotate2 2s linear infinite;
      }

      .box::before {
        content: '';
        position: absolute;
        width: 300px;
        height: 300px;
        top: 0px;
        left: 0px;
        background: repeating-radial-gradient(
            #000,
            #000 50%,
            transparent 50%,
            transparent 60%,
            #000 60%,
            #000 100%
          ),
          repeating-conic-gradient(
            rgb(42, 153, 255) 0,
            rgb(42, 153, 255) 4%,
            transparent 4%,
            transparent 5%
          );
        transform: translateZ(200px);
        animation: rotate1 2s linear infinite;
      }
      .box::after {
        content: '';
        position: absolute;
        width: 300px;
        height: 300px;
        top: 0px;
        left: 0px;
        background: #fff;
        background: repeating-linear-gradient(
          to bottom,
          transparent 0,
          transparent 80px,
          rgba(42, 153, 255, 0.1) 80px,
          rgb(42, 153, 255) 130px
        );
        -webkit-mask: repeating-linear-gradient(
          to right,
          transparent 0px,
          transparent 80px,
          #000 80px,
          #000 82px
        );
        transform: rotateX(90deg);
        animation: line 2s linear infinite;
      }

      @keyframes line {
        0% {
          transform: translateZ(-50px) rotateX(90deg) rotateY(0deg);
        }
        100% {
          transform: translateZ(200px) rotateX(90deg) rotateY(-360deg);
        }
      }

      @keyframes rotate1 {
        0% {
          transform: translateZ(200px) rotateZ(0deg);
        }
        100% {
          transform: translateZ(200px) rotateZ(-720deg);
        }
      }
      @keyframes rotate2 {
        0% {
          transform: rotateZ(0deg);
        }
        100% {
          transform: rotateZ(360deg);
        }
      }
    </style>
  </head>
  <body>
    <div class="box"></div>
    <script>
      var elem = document.querySelector('body')
      var isDragging = false // 用于判断是否正在拖动
      var initialX = 0 // 初始鼠标X坐标
      var initialY = 0 // 初始鼠标Y坐标
      var currentX = 0 // 当前鼠标X坐标
      var currentY = 0 // 当前鼠标Y坐标
      var initialRotationY = 0 // 初始旋转角度(Y轴)
      var initialRotationX = 0 // 初始旋转角度(X轴)

      elem.addEventListener('mousedown', function (e) {
        // 当鼠标按下时
        initialY = e.clientY // 获取初始鼠标Y坐标
        initialRotationY = parseInt(
          getComputedStyle(elem)
            .getPropertyValue('transform')
            .replace(/[^0-9-.,]/g, '')
            .split(',')[4]
        ) // 获取初始旋转角度(Y轴)
        initialRotationX = parseInt(
          getComputedStyle(elem)
            .getPropertyValue('transform')
            .replace(/[^0-9-.,]/g, '')
            .split(',')[5]
        ) // 获取初始旋转角度(X轴)
        isDragging = true // 设置isDragging为true,表示正在拖动
      })

      document.addEventListener('mousemove', function (e) {
        // 当鼠标移动时
        if (isDragging) {
          // 如果正在拖动
          currentY = e.clientY // 获取当前鼠标Y坐标
          var dy = currentY - initialY // Y轴方向移动的距离
          var newRotationY = initialRotationY + dy * -1 // 根据移动方向计算新的旋转角度(Y轴)
          console.log(111, dy, newRotationY)

          elem.style.transform = 'rotateX(' + newRotationY + 'deg)' // 设置元素的旋转角度
        }
      })

      document.addEventListener('mouseup', function () {
        // 当鼠标松开时
        isDragging = false // 设置isDragging为false,表示已经停止拖动
      })
    </script>
  </body>
</html>
相关推荐
格子软件1 小时前
2026年GEO优化系统源码级状态机与多模型调度拆解
java·前端·vue.js·人工智能·vue·geo
HUMHSX2 小时前
Vue 项目启动全流程解析:从入口文件到全局指令注册与页面渲染
前端·javascript·vue.js
有颜有货2 小时前
PMC生产排产的4种算法,一次讲清
java·服务器·前端
小虎牙0072 小时前
Android kotlin图片库Coil源码详解
android·前端
随风一样自由2 小时前
【前端领域】前端开发核心应用场景与落地实践
前端·前端框架
an317423 小时前
弹窗数据流设计的两种高阶架构实践
前端·vue.js·架构
谢尔登3 小时前
【React】 状态管理方案
前端·react.js·前端框架
用户2136610035723 小时前
Vue商品详情与放大镜组件
前端·javascript
半个落月3 小时前
从Tapas小Demo理清localStorage、事件与this
前端·javascript
李明卫杭州3 小时前
Vue2 中 v-model 处理不同数据结构的技巧
前端·javascript·vue.js