CSS 滚屏效果,要比想象中的难

13,160 阅读3分钟

今天有一位掘友问我一个问题:

文字不换行,超出容器,怎么滚动显示?

比如下图中,有的列表项的文字短,有的文字长。

需求是:希望文字长的部分能通过动画滚动显示

我的第一反应是用纯 CSS 做不了。

但是后来冥思苦想一阵子,竟然做到了!

其中涉及了一些有趣 CSS 知识点,在这里分享一下。

1. 如何让文字溢出容器?

work-break 属性是用来处理文字如何换行的,这里使用了不太常用的值 keep-all,表示只能在半角空格或连字符处换行。因为文本里没有这两种特殊字符,因此文本溢出了。

2. 如何让元素宽度随内容而定?

指定了 width 值为 fit-content,它是 CSS3 新的属性值,表示宽度与内容一致。除了 fit-content 之外,还有 max-content、 min-content 和 fill-available。具体含义如有不清楚的,请 google 之。

3. 动起来?

这里我们使用关键帧动画,使其滚动起来先。关键帧定义如下:

@keyframes move {
  0%{
    transform: translateX(0px);
  }
  100%{
    transform: translateX(-100%);
  }
}

具体效果是:

这里可以看出,我们在第 2 步设置元素的宽度为文字自身宽度的原因是,我们动画移动元素是相对于自身的宽度的。

目前效果还是比较粗糙,比如整体移动 100%,我们希望文字尾部与 div 容器内容盒右边卡齐:

由于我们知道父元素的 width 值的(这里是 100px),使用 calc() 就能轻松做到:

@keyframes move {
  0%{
    transform: translateX(0px);
  }
  100%{
    transform: translateX(calc(-100% + 100px));
  }
}

同时再让父元素隐藏溢出部分,效果如下:

4. 如何让短文字不动?

总体上实现了滚动的效果,但是掘友的需求是,长文字滚动,短文字不动的。

说实话,我为这个需求苦恼了小半天。。。

后来,直拍大腿:设置 p 的最小宽度呀!

5. 最后一步,优化动画!

现在的效果,怎么说呢,滚动得太让人闹心,可以让动画滚动开始和结束前,稍微停顿一会儿:

@keyframes move {
  0%, 20%{
    transform: translateX(0px);
  }
  80%,100%{
    transform: translateX(calc(-100% + 100px));
  }
}

前 20% 保持效果不变,后 20% 亦是如此:

还有一个问题,这里我们的动画时间是 3s,我们可以根据其文字长度来设置时间。即,更长的文字需要更长的时间。

但此处,我不知道用 CSS 怎么办,尝试了几次,最终还是放弃了,使用了一小段 JS。

[...document.querySelectorAll('p')].forEach(p => {
	p.style.setProperty('--duration', p.offsetWidth / 100 + 's');
})

其中 --duration 是 CSS 变量(不熟悉的同学请 google 之),p 标签的 animation 属性也需要相应变为:

animation: move var(--duration) linear infinite;

最后,完整代码和完整效果,请看codepen.io/laoyao/pen/…


本文完。

谢谢阅读!

🙏🙏🙏