javascript - 如何用JS制作无限跑马灯?

标签 javascript html css animation linear-interpolation

我正在尝试制作一个可以加速滚动的无限选框,https://altsdigital.com/你可以在这个网站上看到效果,文字说“不是你通常的 SEO 机构”,当你滚动时它会加速。

这是我尝试过的方法,但它不起作用。如果没有重叠,它不会正确循环(请注意页面的左侧,您会注意到文本短暂重叠然后向左平移以产生间隙)我不确定如何修复它:

这是代码(文本仅在“全页” View 中可见):

const lerp = (current, target, factor) => {
    let holder = current * (1 - factor) + target * factor;
    holder = parseFloat(holder).toFixed(3);
    return holder;
};

class LoopingText {
    constructor(DOMElements) {
        this.DOMElements = DOMElements;
        this.lerpingData = {
            counterOne: { current: 0, target: 0 },
            counterTwo: { current: 100, target: 100 },
        };
        this.interpolationFactor = 0.1;
        this.speed = 0.2;
        this.render();
        this.onScroll();
    }

    onScroll() {
        window.addEventListener("scroll", () => {
            this.lerpingData["counterOne"].target += this.speed * 5;
            this.lerpingData["counterTwo"].target += this.speed * 5;
        });
    }

    lerp() {
        for (const counter in this.lerpingData) {
            this.lerpingData[counter].current = lerp(
                this.lerpingData[counter].current,
                this.lerpingData[counter].target,
                this.interpolationFactor
            );
        }

        this.lerpingData["counterOne"].target += this.speed;
        this.lerpingData["counterTwo"].target += this.speed;

        if (this.lerpingData["counterOne"].target < 100) {
            this.DOMElements[0].style.transform = `translate(${this.lerpingData["counterOne"].current}%, 0%)`;
        } else {
            this.lerpingData["counterOne"].current = -100;
            this.lerpingData["counterOne"].target = -100;
        }

        if (this.lerpingData["counterTwo"].target < 100) {
            this.DOMElements[1].style.transform = `translate(${this.lerpingData["counterTwo"].current}%, 0%)`;
        } else {
            this.lerpingData["counterTwo"].current = -100;
            this.lerpingData["counterTwo"].target = -100;
        }
    }

    render() {
        this.lerp();

        window.requestAnimationFrame(() => this.render());
    }
}

let textArray = document.getElementsByClassName("item");
new LoopingText(textArray);
@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: "Poppins";
}

.hero-section {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh;
    overflow: hidden;
    position: relative;
    width: 100%;
}

.loop-container {
    position: relative;
    width: 100%;
    display: flex;
    /* padding-right: 24px; */
}

.item {
    position: absolute;
    font-size: 15rem;
    white-space: nowrap;
    margin: 0;
}

span {
    transition: all 0.2s;
    cursor: default;
}

.hover:hover {
    color: gray;
    transition: all 0.2s;
}
<body>
    <section class="hero-section">
        <div class="loop-container">
            <div class="item">Infinite Horizontal Looping Text</div>
            <div class="item">Infinite Horizontal Looping Text</div>
        </div>
    </section>
    <section class="hero-section">
    </section>
</body>

最佳答案

您的元素是重叠的,因为您不允许在元素应该切换位置时进行任何跳动差异

当前值应该永远等于目标值。如果值匹配,则当前值需要 catch target — 导致不稳定的移动和错误的计算,另外加剧了两个应该完美的兄弟元素同步以提供即时的回弹,被视为流畅的连续运动。

解决方案

  • 不是独立地为两个(或更多)子级设置动画,
    只为父级.loop-container设置动画。
  • 容器应与一个子元素一样
  • 使用 position: absolute; 将一个子元素“推”到最左边;左:-100%
  • 允许目标值总是大于当前值:
    目标 值大于 100 — 将 current 设置为两个值的负差,将 target 设置为0

演示时间:

const lerp = (current, target, factor) => current * (1 - factor) + target * factor;

class LoopingText {
  constructor(el) {
    this.el = el;
    this.lerp = {current: 0, target: 0};
    this.interpolationFactor = 0.1;
    this.speed = 0.2;
    this.direction = -1; // -1 (to-left), 1 (to-right) 
    
    // Init
    this.el.style.cssText = `position: relative; display: inline-flex; white-space: nowrap;`;
    this.el.children[1].style.cssText = `position: absolute; left: ${100 * -this.direction}%;`;
    this.events();
    this.render();
  }

  events() {
    window.addEventListener("scroll", () => this.lerp.target += this.speed * 5);
  }

  animate() {
    this.lerp.target += this.speed;
    this.lerp.current = lerp(this.lerp.current, this.lerp.target, this.interpolationFactor);
    
    if (this.lerp.target > 100) {
      this.lerp.current -= this.lerp.target;
      this.lerp.target = 0;
    }

    const x = this.lerp.current * this.direction;
    this.el.style.transform = `translateX(${x}%)`;
  }

  render() {
    this.animate();
    window.requestAnimationFrame(() => this.render());
  }
}

document.querySelectorAll(".loop-container").forEach(el => new LoopingText(el));
/* QuickReset */ * { margin: 0; box-sizing: border-box; }

body { min-height: 400vh;  /* force some scrollbars */ }

.hero-section {
  position: relative;
  top: 50vh;
  overflow: hidden;
  font: 900 9vw/1 sans-serif;
  min-height: 100vh;
}
<section class="hero-section">
  <div class="loop-container">
    <div class="item">Infinite Horizontal Looping Text&nbsp;</div>
    <div class="item">Infinite Horizontal Looping Text&nbsp;</div>
  </div>
</section>

附言:

当动画时,(除非你想要一个静态的/不可移动的元素)你不应该将元素转换放在 if/else 逻辑中。该元素应始终接收更新的转换。仅将您实际想要修改的值放入条件逻辑中(就像我在上面的示例中所做的那样)。

关于javascript - 如何用JS制作无限跑马灯?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71165923/

相关文章:

css - 如何在 Webpack 中的 HTML 文件中要求 .css、.js 文件

javascript - 如何制作左右面板,并能够在它们之间滑动分隔线?

css - asp.net mvc4中如何设置背景图片

html - 将 <div> 与图像放在同一行

javascript - 在 CDN 上托管皮肤文件时可能出现 Javascript 问题

javascript - css 在右侧对齐多行 contenteditable 内容

javascript - 为什么语法错误出现在我的 angularjs 元素中

html - 每行中具有相同高度单元格的表格布局

javascript - 在 react 功能组件中 setState 的功能语法有什么用?

javascript - 使用javascript直接将剪贴板图像保存到客户端