一、名称来源
走马灯古称蟠螭灯(秦汉)、仙音烛和转鹭灯(唐)、马骑灯(宋),汉族特色工艺品 [1] ,亦是传统节日玩具之一,属于灯笼的一种。常见于元夕、元宵、中秋等节日。
灯内点上蜡烛,烛产生的热力造成气流,令轮轴转动。轮轴上有剪纸,烛光将剪纸的影投射在屏上,图象便不断走动。因多在灯各个面上绘制古代武将骑马的图画,而灯转动时看起来好像几个人你追我赶一样,故名走马灯。
百度百科
marquee 大帐篷
a large tent used for eating and drinking in at events held mainly outside that involve a lot of people
剑桥英语词典
二、应用
电脑中的跑马灯
,跑马灯在编程中,通常指有时需要用一矩形条显示少量用户特别关心的信息,这条信息串首尾相连,向一个方向循环滚动。
另外,在日常生活中也随处可见,如 新闻播报
股票行情
电子广告牌
等。
三、实现方式
1、HTML (不推荐)
<marquee>
元素已经 过时,请不要再使用。尽管一些浏览器仍然支持它,但它不是必须的。此外,使用这个元素基本上是你可以对你的用户做最糟糕的事情之一,所以请不要这样做。
根据 MDN 的描述,<marquee>
标签已经不被推荐使用,虽然为了向后兼容在部分浏览器上该标签依然有效,但未来可能会被移除掉。至于可能的原因,有两个:
- HTML的本质是标记语言,应当负责文档结构的部分,而不是表现形式;
- 已经有了更好的CSS动画实现方案
相关的讨论:
why-is-marquee-deprecated-and-what-is-the-best-alternative
因此不推荐使用此种方式实现。
2、CSS
2.1 基础实现:
- HTML部分:
<div class="marquee">
<span>I believe that the times ahead will be radically different from the times we have experienced so far in our lifetimes, though similar to many other times in history.</span>
</div>
外部容器 <div>
控制可视宽度,内部元素 <span>
展示具体内容
- CSS部分
.marquee {
width: 800px;
margin: 0 auto;
background: lightblue;
overflow: hidden;
white-space: nowrap;
span {
display: inline-block;
padding-left: 100%;
animation: marquee 15s linear infinite;
&:hover {
animation-play-state: paused;
}
}
}
@keyframes marquee {
0% { transform: translate(0,0); }
100% { transform: translate(-100%, 0); }
}
这里选择使用 transform
而不是 left
,是因为在动画中使用 left
会导致浏览器重绘,相比 transform
更耗性能,有兴趣可以阅读 Should I do the animation with Left or TranslateX
另外,使用 padding-left: 100%
百分比,而非固定宽度,使得改变文字内容时,无需重新设置宽度,而只需调整动画时长即可。
此种实现方式基本可以满足大部分需求。只是,在一次滚动结束后,下次滚动开始前,中间会有大量的留白。
2.2 无缝连接
为了解决两次滚动中间的留白,我们可以将内部元素复制一份,通过将他们首位相接的方式,来实现无缝连接的效果。
<div class="marquee">
<span> I believe that the times ahead will be radically different from the times we have experienced so far in our lifetimes, though similar to many other times in history.</span>
<span> I believe that the times ahead will be radically different from the times we have experienced so far in our lifetimes, though similar to many other times in history.</span>
</div>
.marquee {
width: 800px;
margin: 0 auto;
background: lightblue;
overflow: hidden;
white-space: nowrap;
span {
display: inline-block;
padding-left: 100%;
animation: marquee 15s linear infinite;
}
&:hover span{
animation-play-state: paused;
}
}
@keyframes marquee {
0% { transform: translate(0,0); }
100% { transform: translate(-100%, 0); }
}
无缝连接的实现原理如下:
- 两个内部元素从各自初始位置(相隔100%宽度)同时滚动;
- 当内部元素1离开展示区时,内部元素2刚好进入展示区;
- 当内部元素2离开展示区时,内部元素1此时刚好播完一次动画,开始播放下一次动画,也就接上了内部元素2的尾巴,实现了无缝连接。
3、JavaScript
<div id="marquee">
<span>I believe that the times ahead will be radically different from the times we have experienced so far in our lifetimes, though similar to many other times in history.</span>
</div>
#marquee {
width: 800px;
margin: 0 auto;
background: lightblue;
padding: 8px 16px;
overflow: hidden;
> span {
display: inline-block;
white-space: nowrap;
position: relative;
}
}
window.onload = () => {
let box = document.querySelector("#marquee");
let text = document.querySelector("#marquee > span");
let boxWidth = box.offsetWidth; // offsetWidth = width + padding + margin
let textWidth = text.offsetWidth;
if (textWidth > boxWidth) {
startAnimation();
}
function startAnimation() {
return step();
}
function step() {
let left = getLeft(text);
setLeft(text, --left);
if (left < -textWidth) {
setLeft(text, boxWidth);
}
setTimeout(step, 30);
}
function getLeft(box) {
let leftStr = box.style.left;
if (leftStr === '') return 0;
return leftStr.substr(0, leftStr.length-2) - 0;
}
function setLeft(box, left = 0) {
box.style.left = `${left}px`;
}
};
实现原理如下:
- 首先判断,若文字元素
offsetWidth
大于容器元素offsetWidth
,才继续下面的流程 - 添加轮询定时器,每隔一段时间向左移动文字元素,直到文字元素完全离开容器(即
left
属性小于容器的宽度),则重置left
属性为容器的宽度(即将文字元素左侧对齐容器右侧)
四、小结
本文主要介绍:
- 跑马灯的来源
- 几种实现方式
在日常开发中,CSS 的实现方式基本可以满足大部分场景,而且代码方便维护,建议优先考虑。
当然,除了使用 left
和 transform
外,还有操纵其他属性来实现:
- margin-left (✨)
- left (✨✨)
- transform( ✨✨✨)
- scrollLeft(有取值限制)
- padding-left(不允许负值)
- 改变文字数量(鬼才)
感谢您的阅读,欢迎留言交流。
完。