如何用JS制作无限跑马灯?
How do I make an Infinite marquee with JS?
我正在尝试制作一个可以加快滚动速度的 Infinite marquee,https://altsdigital.com/ 你可以在这个网站上看到效果,文字说“不是你通常的 SEO 机构”,当你滚动它时速度向上。
以下是我尝试过的方法,但它不起作用。它没有重叠就不能正确循环(请注意页面的左侧,您会注意到文本短暂重叠然后向左平移以产生间隙)我不确定如何修复它:
这是代码(文本仅在“全页”视图中可见):
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>
你的项目是重叠的,因为你不允许在项目应该交换位置时进行任何勒平差异。
current
值应该永远不会 等于target
值。如果值匹配,则当前值需要 赶上 target
— 给出不稳定的运动和错误的计算,另外加剧了两个应该完美的兄弟元素同步给予即时 snap-back,被视为流畅的连续运动。
解决方案
- 不是独立地为两个(或更多)子级设置动画,
只为父级设置动画.loop-container
。
- 容器应与 一个子元素 一样宽。
- 使用
position: absolute; left: -100%
将一个子元素“推”到最左边
- 允许
target
值始终 大于 比 current
值:
当 target
值大于 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.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;
}
this.el.style.transform = `translateX(${this.lerp.current}%)`;
}
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;
overflow: hidden;
display: flex;
min-height: 100vh;
}
.loop-container {
margin: auto 0;
white-space: nowrap;
font: 900 9vw/1 sans-serif;
}
.item:first-child {
position: absolute;
left: -100%;
top: 0;
}
<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>
PS:
制作动画时,(除非你想要一个静态/不可移动的元素)你不应该将元素转换放在 if/else 逻辑中。该元素应始终接收更新的转换。仅将您实际想要修改的值放入条件逻辑中(就像我在上面的示例中所做的那样)。
我正在尝试制作一个可以加快滚动速度的 Infinite marquee,https://altsdigital.com/ 你可以在这个网站上看到效果,文字说“不是你通常的 SEO 机构”,当你滚动它时速度向上。
以下是我尝试过的方法,但它不起作用。它没有重叠就不能正确循环(请注意页面的左侧,您会注意到文本短暂重叠然后向左平移以产生间隙)我不确定如何修复它:
这是代码(文本仅在“全页”视图中可见):
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>
你的项目是重叠的,因为你不允许在项目应该交换位置时进行任何勒平差异。
current
值应该永远不会 等于target
值。如果值匹配,则当前值需要 赶上 target
— 给出不稳定的运动和错误的计算,另外加剧了两个应该完美的兄弟元素同步给予即时 snap-back,被视为流畅的连续运动。
解决方案
- 不是独立地为两个(或更多)子级设置动画,
只为父级设置动画.loop-container
。 - 容器应与 一个子元素 一样宽。
- 使用
position: absolute; left: -100%
将一个子元素“推”到最左边
- 允许
target
值始终 大于 比current
值:
当target
值大于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.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;
}
this.el.style.transform = `translateX(${this.lerp.current}%)`;
}
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;
overflow: hidden;
display: flex;
min-height: 100vh;
}
.loop-container {
margin: auto 0;
white-space: nowrap;
font: 900 9vw/1 sans-serif;
}
.item:first-child {
position: absolute;
left: -100%;
top: 0;
}
<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>
PS:
制作动画时,(除非你想要一个静态/不可移动的元素)你不应该将元素转换放在 if/else 逻辑中。该元素应始终接收更新的转换。仅将您实际想要修改的值放入条件逻辑中(就像我在上面的示例中所做的那样)。