无限循环滚动不定宽横幅

如题。

复制代码
<html lang="zh-CN">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>不定宽无限滚动</title>
  <style>
    * {
      -webkit-box-sizing: border-box;
      box-sizing: border-box;
    }

    body {
      background: darkcyan;
      margin: 0;
    }

    .no-wrap {
      white-space: nowrap !important;
    }

    .container {
      /* max-width: 992px; */
      width: 100vw;
      margin: 0 auto;
      overflow: hidden;
    }

    .grid {
      display: block;
      font-size: 0;
    }

    .cell {
      display: inline-block;
      font-size: 2rem;
      font-weight: 700;
      position: relative;
      width: 100%;
      height: 30vh;
      padding: 1rem .5rem;
      text-align: center;
      cursor: pointer;
    }

    .cell .content {
      display: block;
      position: absolute;
      top: .5rem;
      bottom: .5rem;
      left: .5rem;
      right: .5rem;
      padding: 1rem;
      color: #555;
      background: #fff;
      box-shadow: 0 2px 10px -10px #ccc;
    }

    .cell.w16 {
      width: 16%;
      width: calc(100% / 6)
    }

    .cell.w25 {
      width: 25%
    }

    .cell.w33 {
      width: 33%;
      width: calc(100% / 3)
    }

    .cell.w50 {
      width: 50%
    }

    .cell.w66 {
      width: 66%;
      width: calc(100% / (3 / 2))
    }

    .cell.w75 {
      width: 75%
    }

    .cell.w83 {
      width: 83%;
      width: calc(100% / (6 / 5))
    }

    .cell.w100 {
      width: 100%
    }

    h1,
    h2 {
      text-align: center;
      color: #fff;
    }

    h1 {
      font-size: 3rem;
      margin-bottom: .15rem;
    }

    h2 {
      font-size: 1.3rem;
      margin-top: .15rem;
      margin-bottom: 15vh;
    }
  </style>
</head>

<body>
  <h1>无限滚动横幅</h1>
  <h2>( hover播放/暂停 )</h2>
  <div class="container">
    <div class="grid infinite-banner">
      <div class="cell w66">
        <div class="content">
          开始
        </div>
      </div>
      <div class="cell w33">
        <div class="content">
          1
        </div>
      </div>
      <div class="cell w25">
        <div class="content">
          2
        </div>
      </div>
      <div class="cell w25">
        <div class="content">
          3
        </div>
      </div>
      <div class="cell w50">
        <div class="content">
          4
        </div>
      </div>
      <div class="cell w100">
        <div class="content">
          5
        </div>
      </div>
      <div class="cell w16">
        <div class="content">
          6
        </div>
      </div>
      <div class="cell w83">
        <div class="content">
          7
        </div>
      </div>
      <div class="cell w25">
        <div class="content">
          8
        </div>
      </div>
      <div class="cell w75">
        <div class="content">
          9
        </div>
      </div>
      <div class="cell w66">
        <div class="content">
          10
        </div>
      </div>
      <div class="cell w33">
        <div class="content">
          结束
        </div>
      </div>
    </div>
  </div>
  <script>
    // 动画对象
    let bannerAnimation

    // 获取滚动显示元素和其子元素
    const bannerEl = document.querySelector('.grid.infinite-banner')
    const cells = bannerEl.querySelectorAll('.cell')

    /**
     * 创建无限滚动动画
     * 检查元素是否具有animate方法,如果没有则不执行动画
     * 计算所有子元素的总宽度,并基于这个宽度创建一个无限循环的线性动画
     */
    const createBannerAnimation = () => {
      // 检查bannerEl元素是否存在animate方法
      if ('animate' in bannerEl && typeof bannerEl.animate === 'function') {
        // 如果已有动画,取消之前的动画
        if (bannerAnimation) bannerAnimation.cancel()

        // 避免文本自动换行影响动画效果
        bannerEl.style.whiteSpace = 'nowrap'

        // 初始化位移变量,用于计算动画的最终位移量
        let displacement = 0
        // 遍历所有子元素,累加宽度以计算总位移量
        for (const cell of cells) displacement += cell.clientWidth
        // 调整位移量
        displacement = (displacement - bannerEl.clientWidth) << 0

        // 创建并启动动画,使轮播图从右向左滚动
        bannerAnimation = bannerEl.animate(
          [
            { transform: 'matrix(1, 0, 0, 1, 0, 0)', offset: 0 },
            {
              transform: `matrix(1, 0, 0, 1, ${-displacement}, 0)`,
              offset: 1
            }
          ],
          {
            duration: cells.length * 3000, // 动画总时长
            easing: 'linear', // 匀速动画效果
            delay: 0, // 无延迟启动
            iterations: Infinity, // 无限循环
            direction: 'normal', // 'reverse'
            fill: 'forwards' // 动画结束后保持最终状态
          }
        )
      }
    }

    /**
     * 播放滚动动画
     */
    const playBannerAnimation = () => {
      if (bannerAnimation && bannerAnimation.playState === 'paused')
        bannerAnimation.play()
    }

    /**
     * 暂停滚动动画
     */
    const pauseBannerAnimation = () => {
      if (bannerAnimation && bannerAnimation.playState === 'running')
        bannerAnimation.pause()
    }

    /**
     * 防抖
     */
    const debounce = (func) => {
      let scheduled, context, args;
      return () => {
        context = this; args = Array.from(arguments);
        if (scheduled) window.cancelAnimationFrame(scheduled);
        scheduled = window.requestAnimationFrame(() => {
          func.apply(context, args);
          scheduled = null;
        });
      };
    };

    // 创建动画,添加鼠标事件
    if ('animate' in bannerEl && typeof bannerEl.animate === 'function') {
      createBannerAnimation()
      bannerEl.addEventListener('mouseenter', pauseBannerAnimation, false)
      bannerEl.addEventListener('mouseleave', playBannerAnimation, false)
      window.addEventListener(
        'resize',
        debounce(createBannerAnimation),
        false
      )
    } else {
      // 浏览器不支持 Web Animation API
    }
  </script>
</body>

</html>
相关推荐
ZC跨境爬虫5 小时前
跟着 MDN 学 HTML day_9:(信件语义标记)
前端·css·笔记·ui·html
前端老石人5 小时前
HTML 字符引用完全指南
开发语言·前端·html
matlab_xiaowang5 小时前
Redux 入门:JavaScript 可预测状态管理库
开发语言·javascript·其他·ecmascript
幼儿园技术家5 小时前
前端如何设计权限系统(RBAC / ABAC)?
前端
前端摸鱼匠7 小时前
Vue 3 的v-bind合并行为:讲解v-bind与普通属性合并的规则
前端·javascript·vue.js·前端框架·ecmascript
REDcker7 小时前
浏览器端Web程序性能分析与优化实战 DevTools指标与工程清单
开发语言·前端·javascript·vue·ecmascript·php·js
donecoding9 小时前
一个 sudo 引发的血案:npm 全局包权限错乱彻底修复
前端·node.js·前端工程化
风骏时光牛马9 小时前
Raku正则匹配与数据批量处理实操案例
前端
nbwenren9 小时前
2026实测:Gemini 3 镜像站视觉能力实践——拍照原型图,一键生成 HTML+CSS 代码
前端·css·html
Lee川9 小时前
Prisma 实战指南:像搭积木一样设计古诗词数据库
前端·数据库·后端