css+div+js 实现元素沿 圆/椭圆 轨迹运动

发布于:2025-08-10 ⋅ 阅读:(22) ⋅ 点赞:(0)

css+div+js 实现元素沿 圆/椭圆 轨迹运动

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>实现元素沿 圆/椭圆 轨迹运动</title>
  <style>
    body {
      height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    .circle {
      position: relative;
      width: 400px;
      height: 300px;
      border: 1px solid red;
      border-radius: 50%;
    }

    .dot {
      position: absolute;
      width: 40px;
      height: 40px;
      text-align: center;
      line-height: 40px;
      border: 1px solid red;
      border-radius: 50%;
    }
  </style>
</head>

<body>
  <div class="circle">
    <!-- <div class="dot">点</div> -->
  </div>
  <script>
    /**
     * 根据角度和半径计算圆上的点坐标
     * @param {number} angle - 角度(0-360度)从正右方开始
     * @param {number} radius - 半径
     * @param {number} [centerX=0] - 圆心x坐标,默认为0
     * @param {number} [centerY=0] - 圆心y坐标,默认为0
     * @returns {Object} 包含x和y坐标的对象
     */
    function getPointOnCircle(angle, radius, centerX = 0, centerY = 0) {
      // angle -= 180 // 从正左方开始
      // 将角度转换为弧度(Math.cos和Math.sin使用弧度)
      const radians = angle * Math.PI / 180

      return {
        x: centerX + radius * Math.cos(radians),
        y: centerY + radius * Math.sin(radians)
      }
    }

    /**
     * 根据角度获取椭圆上的点
     * @param {number} a - 椭圆的长半轴(x轴方向)
     * @param {number} b - 椭圆的短半轴(y轴方向)
     * @param {number} angle - 角度(以度为单位)
     * @param {number} [centerX=0] - 椭圆中心的x坐标(可选,默认为0)
     * @param {number} [centerY=0] - 椭圆中心的y坐标(可选,默认为0)
     * @returns {Object} 包含x和y坐标的对象
     */
    function getPointOnEllipse(a, b, angle, centerX = 0, centerY = 0) {
      // 将角度转换为弧度
      const radians = angle * Math.PI / 180

      // 计算椭圆上的点坐标
      const x = centerX + a * Math.cos(radians)
      const y = centerY + b * Math.sin(radians)

      return { x, y }
    }


    let itemDom = []
    let staticDom = []
    let data = {}
    function toAnimate() {
      // 动画之前存储上一个dom数据
      let last = itemDom.slice(-1)[0]
      if (last) {
        data.deg = last.deg
        data.startDeg = last.startDeg
        data.transform = last.style.transform
        data.index = parseInt(last.innerText)
      }
      // 控制时间 动画频率
      // let step = 360 / (6/* 秒 */ * 60)
      let step = 0.5
      let count = 36
      function animate() {
        itemDom.forEach(dom => {
          let { x, y } = getPointOnEllipse(200, 150, dom.deg, 180, 130)
          dom.style.transform = `translate(${x}px,${y}px)`
          dom.deg += step
          if (dom.deg > 360) {
            dom.deg = 0
          }
        })
        count -= step
        if (count <= 0) {
          let dom = itemDom.shift()
          circleDom.removeChild(dom)
          staticDom.push(dom)
          if ((staticDom.length + itemDom.length) < 12/* 原始数据量,按设计视图最多挂载5个dom */) {
            let dotDom = document.createElement('div')
            dotDom.classList.add('dot')
            dotDom.innerText = parseInt(data.index) + 1
            dotDom.deg = data.deg
            dotDom.startDeg = data.startDeg
            dotDom.style.transform = data.transform
            itemDom.push(dotDom)
            circleDom.append(dotDom)
          } else {
            let dotDom = staticDom.shift()
            dotDom.deg = data.deg
            dotDom.startDeg = data.startDeg
            dotDom.style.transform = data.transform
            itemDom.push(dotDom)
            circleDom.append(dotDom)
          }
          setTimeout(() => {
            toAnimate()
          }, 3000 /* 动画间隔时间 */)
        } else {
          window.requestAnimationFrame(animate)
        }
      }
      window.requestAnimationFrame(animate)
    }

    let circleDom = document.querySelector('.circle')
    function generate(itemCount) {
      let fullCount = 5
      let start = -215.5
      for (let i = 0; i < fullCount; i++) {
        let dotDom = document.createElement('div')
        dotDom.classList.add('dot')
        dotDom.innerText = i
        let { x, y } = getPointOnEllipse(200, 150, start, 180, 130)
        dotDom.deg = start
        dotDom.startDeg = start
        dotDom.style.transform = `translate(${x}px,${y}px)`
        itemDom.push(dotDom)
        circleDom.append(dotDom)
        start -= 36
        if (start < -360) {
          start = start - -360
        }
      }
    }
    generate(12)
    // 当量小于等于4个时则不进行动画
    toAnimate()
  </script>
</body>

</html>

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到