广州总部电话:020-85564311
广州总部电话:020-85564311
20年
互联网应用服务商
请输入搜索关键词
知识库 知识库

优网知识库

探索行业前沿,共享知识宝库

一行CSS实现无限滚动 Logo 墙:现代 CSS 的魔法

发布日期:2025-08-18 17:46:07 浏览次数: 810 来源:前端小石匠
推荐语
用现代CSS魔法轻松打造无限滚动Logo墙,仅需10行代码实现响应式效果!

核心内容:
1. 现代CSS新特性(shape()、sibling-index()等)的巧妙运用
2. 无需JavaScript的纯CSS实现方案解析
3. 与传统实现方法的对比及性能优势
小优 网站建设顾问
专业来源于二十年的积累,用心让我们做到更好!

在网页开发中,带有无限循环滑动动画的logo集合是一个经典组件。从古老(现已废弃)的<marquee>元素开始,我们可以找到无数示例和实现方式。几年前我也曾写过一篇相关文章。

**"为什么还要再写一篇?"**你可能会问。CSS不断发展,涌现出许多强大新特性,因此我始终在寻找优化改进的空间。现在我们就将运用一些新的CSS特性来实现这个效果。

截至本文撰写时,只有基于Chrome的浏览器完全支持我们将使用的特性,包括shape()sibling-index()sibling-count()等功能。

在上面的演示中,我们实现了一个支持任意数量图片的无限跑马灯动画。只需在HTML中添加任意数量的元素,完全无需修改CSS代码。通过调整一个变量就能轻松控制可见图片数量,而且效果是响应式的——调整屏幕尺寸时可以看到平滑的适配效果。

你可能会认为代码冗长且充满复杂计算,但实际上只需不到10行CSS代码,完全不需要JavaScript:

.container {
--s150px/* 图片尺寸 */
--d8s;    /* 动画时长 */
--n4;     /* 可见图片数量 */

display: flex;
overflow: hidden;
}
img {
widthvar(--s);
offsetshape(from calc(var(--s)/-250%,hline by calc(sibling-count()*max(100%/var(--n),var(--s))));
animation: x var(--d) linear infinite calc(-1*sibling-index()*var(--d)/sibling-count());
}
@keyframes x { 
to { offset-distance100%; }
}

乍看之下可能有些复杂,尤其是那个奇怪的offset属性!别盯着它看太久,我们会一起剖析它,到文章结束时它就会显得相当简单了。

实现思路

创建跑马灯效果最棘手的部分是实现循环动画——每个元素需要"跳转"回起点重新滑动。早期的实现会复制元素来模拟无限动画,但这不是个好方法,因为它需要操作HTML,可能引发可访问性和性能问题。

一些现代实现依赖于复杂的平移动画,在元素移出可视区域时(用户看不见)创建"跳转"效果,同时在可视区域内保持连续运动。这种方法很完美,但需要复杂计算,且可能依赖于HTML中的元素数量。

如果我们能有一种原生方法创建带有"跳转"的连续动画,同时适用于任意数量元素就完美了。第一部分是可以实现的,我们甚至不需要现代CSS——可以使用offset结合path(),其中路径是一条直线。

在path中,我使用SVG语法定义一条线,然后通过将offset-distance从0%动画到100%来沿该线移动图像。初看很完美,但这种方法不够灵活,因为path()只接受硬编码的像素值。

为了克服path()的限制,我们将使用新的shape()函数!以下是规范中的描述:

shape()函数使用一组与path()大致等效的命令,但采用更标准的CSS语法,并允许完整的CSS功能,如额外单位和数学函数...从这个意义上说,shape()是path()的超集。

我们将使用shape()而非path()来绘制线条,以便利用CSS功能并根据元素数量控制线条。

具体实现

从HTML结构开始——容器内包含一组图片:

<div class="container">
  <img src="">
  <img src="">
  <!-- 任意数量的图片 -->
</div>

我们将容器设为flex布局,移除图片间的默认间距,并确保即使容器较小也不会换行(记住flex-wrap默认是nowrap)。

假设我们想同时显示N张图片,需要将容器宽度设为N × 图片尺寸

.container {
--s100px/* 图片尺寸 */
--n4;     /* 可见图片数量 */

display: flex;
widthcalc(var(--n) * var(--s));
overflow: hidden;
}
img {
widthvar(--s);
}

要实现连续动画,线条长度需要等于图片总数乘以单张图片尺寸。我们可以在图片元素上定义offset属性,并利用新的sibling-count()获取图片总数:

offsetshape(from X Y, hline by calc(sibling-count() * var(--s)));

尝试将X Y设为0 0,会发现所有图片堆叠在一起且位置偏移。这是因为offset应用于子元素(图片),但参考系是父容器。0 0表示线条起点在父容器的左上角。

通过将线条位置调整为0 50%,可以把图片带入容器内:

offsetshape(from 0 50%, hline by calc(sibling-count() * var(--s)));

再将X值设为-S/2,线条起点移到容器外,就看不到图片的"跳转"了:

offsetshape(from calc(var(--s)/-250%, hline by calc(sibling-count() * var(--s)));

为了解决图片重叠问题,需要为每张图片设置不同的延迟。我们可以利用sibling-index()获取每张图片在容器中的索引,从而为每张图片设置不同的延迟:

animation
  x var(--d) linear infinite 
  calc(-1*sibling-index()*var(--d)/sibling-count());

最终完整代码如下:

.container {
--s100px/* 图片尺寸 */
--d4s;    /* 动画时长 */
--n4;     /* 可见图片数量 */

display: flex;
widthcalc(var(--n) * var(--s));
overflow: hidden;
}
img {
widthvar(--s);
offsetshape(from calc(var(--s)/-250%, hline by calc(sibling-count() * var(--s)));
animation: x var(--d) linear infinite calc(-1*sibling-index()*var(--d)/sibling-count());
}
@keyframes x { 
to {offset-distance100%}
}

实现响应式

前面的例子中我们固定了容器宽度来显示指定数量的图片,但如果容器宽度未知如何实现响应式?我们希望在不固定宽度的情况下同时显示N张图片。

关键观察是:如果容器宽度大于N×S,图片间会有空隙,因此shape()定义的线条需要更长以包含额外空间。线条新长度应为:

hline by calc(sibling-count() * 100% / var(--n))

因为offset以父容器为参考系,100%就代表容器宽度。更新后的offset值为:

shape(from calc(var(--s)/-250%, hline by calc(sibling-count() * 100% / var(--n)));

现在动画已经是响应式的了。但如果容器太小,图片会重叠。我们可以结合之前的代码,确保线条长度至少等于图片总数乘以单张图片尺寸:

max(sibling-count() * 100%/var(--n), sibling-count() * var(--s))

进一步优化代码:

calc(sibling-count() * max(100%/var(--n),var(--s)))

还可以添加一个小值作为图片间的最小间距:

calc(sibling-count() * max(100%/var(--n),var(--s) + 10px))

这样我们就实现了一个完全响应式的现代CSS跑马灯动画!

更多示例

虽然我们使用图片来解释这项技术,但它可以轻松扩展到任何类型的内容,唯一要求/限制是项目宽度必须相同。

我们可以创建文本动画:

<div class="container">
  <div>Text 1</div>
  <div>Text 2</div>
  <!-- 更多文本项 -->
</div>

或者更复杂的图文组合:

<div class="container">
  <div class="item">
    <img src="...">
    <p>Description 1</p>
</div>
<div class="item">
    <img src="...">
    <p>Description 2</p>
</div>
<!-- 更多项目 -->
</div>

在这些例子中,我们使用flex-shrink: 0来避免flex项目在容器变小时的默认收缩效果(图片不会收缩到小于定义尺寸,所以之前没有这个问题)。

总结

虽然你们中的许多人可能永远不需要跑马灯动画,但这是一个探索现代CSS特性的好机会,比如shape()sibling-*()函数。更不用说CSS变量、calc()max()等的使用——尽管它们更常见,但我仍然认为属于现代CSS的一部分。

使用min()max()并不总是那么直观,但我有一个小教程可以帮助你判断何时使用哪个。


· · ·

原文链接:https://frontendmasters.com/blog/infinite-marquee-animation-using-modern-css/ 

作者:Temani Afif

优网科技,优秀企业首选的互联网供应服务商

优网科技秉承"专业团队、品质服务" 的经营理念,诚信务实的服务了近万家客户,成为众多世界500强、集团和上市公司的长期合作伙伴!

优网科技成立于2001年,擅长网站建设、网站与各类业务系统深度整合,致力于提供完善的企业互联网解决方案。优网科技提供PC端网站建设(品牌展示型、官方门户型、营销商务型、电子商务型、信息门户型、微信小程序定制开发、移动端应用(手机站APP开发)、微信定制开发(微信官网、微信商城、企业微信)等一系列互联网应用服务。


我要投稿

姓名

文章链接

提交即表示你已阅读并同意《个人信息保护声明》

专属顾问 专属顾问
扫码咨询您的优网专属顾问!
专属顾问
马上咨询
联系专属顾问
联系专属顾问
联系专属顾问
扫一扫马上咨询
扫一扫马上咨询

扫一扫马上咨询

和我们在线交谈!