前言
在日常项目中,需要实现文本【横向】或【纵向】滚动,
- 无逢滚动(从左边界消失的同时立即从右边界出来)------ 【贪吃蛇效果】
- 距离一定的位置进行滚动(文本从容器消失,等待结尾文本距离容器多少距离,才开始从右侧出来)
- 头尾相接 ------ 内容超出容器,但是尾部结束时,头部相隔一定距离同时出现在容器;
一、无逢滚动 --- 已知内容宽度不会超过容器宽度 (纯CSS实现)
会增加额外相同的dom元素
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style lang="scss">
.wrapper {
box-sizing: border-box;
position: absolute;
top: 50%;
left: 30%;
transform: translate(-20%, -50%);
width: 600px;
height: 60px;
overflow: hidden;
border: 4px solid red;
border-radius: 5px;
}
.marquee-wrapper {
display: flex;
align-items: center;
width: 200%;
height: 100%;
transform: translateX(0);
/*animation: move 6s infinite linear;*/
animation-delay: 5s;
/*text-shadow: 1325px 0;*/
}
.marquee-text {
width: 100%;
font-size: 20px;
white-space: nowrap;
/*background-color: red;*/
}
.marquee-text:nth-child(2) {
color: red;
}
@keyframes move {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
</style>
</head>
<body>
<div class="wrapper">
<div class="marquee-wrapper">
<div class="marquee-text">
这是第一行横向滚动的提示文字信息
</div>
<div class="marquee-text">
这是第一行横向滚动的提示文字信息
</div>
</div>
</div>
</div>
</body>
</html>
二、距离一定的位置进行滚动 --- 内容宽度不定 (JS + CSS实现)
不会增加额外相同的dom元素
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style lang="scss">
:root {
--start-position: 0;
}
.marquee-wrapper {
box-sizing: border-box;
position: absolute;
top: 50%;
left: 30%;
transform: translate(-20%, -50%);
display: flex;
align-items: center;
width: 600px;
height: 60px;
overflow: hidden;
border: 4px solid red;
border-radius: 5px;
}
/*.marquee-wrapper {*/
/* display: flex;*/
/* align-items: center;*/
/* width: auto;*/
/* height: 100%;*/
/*}*/
.marquee-text {
width: auto;
font-size: 20px;
white-space: nowrap;
}
.marquee-text.moveFirst {
animation: moveFirst linear forwards;
animation-delay: 3s;
}
.marquee-text.moveInfinite {
animation: moveInfinite linear infinite;
}
@keyframes moveFirst {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-100%);
}
}
@keyframes moveInfinite {
0% {
transform: translateX(var(--start-position));
}
100% {
transform: translateX(-100%);
}
}
</style>
</head>
<body>
<div class="marquee-wrapper">
<div class="marquee-text">
这是第一行横向滚动的提示文字信息1这是第一行横向滚动的提示文字信息2。
</div>
</div>
<script>
const container = document.querySelector('.marquee-wrapper');
const marqueeText = document.querySelector('.marquee-text');
const containerWidth = container.offsetWidth
const marqueeTextWidth = marqueeText.offsetWidth;
const speed = 100; // 滚动速度,单位 px/s
const firstAnimationDuration = marqueeTextWidth / speed; // 第一次动画持续时间
const animationDuration = (marqueeTextWidth + containerWidth) / speed; // 动画持续时间
if (marqueeTextWidth > containerWidth) {
marqueeText.classList.add('moveFirst');
marqueeText.style.animationDuration = `${firstAnimationDuration}s`;
// 注意⚠️:第一次动画执行不能是infinite,否则不会监听到动画结束
marqueeText.addEventListener('animationend', () => {
marqueeText.classList.remove('moveFirst');
marqueeText.classList.add('moveInfinite');
// 设置第二次从什么位置开始执行动画
document.documentElement.style.setProperty('--start-position', containerWidth + 'px');
// marqueeText.style.setProperty('--start-position', containerWidth + 'px');
marqueeText.style.animationDuration = `${animationDuration}s`;
})
}
</script>
</body>
</html>
三、头尾相接 ------ 元素与元素间有一定的间距
- CSS + JS实现
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style lang="scss">
:root {
--width: 0;
}
.marquee-wrapper {
box-sizing: border-box;
position: absolute;
top: 50%;
left: 30%;
transform: translate(-20%, -50%);
display: flex;
align-items: center;
width: 600px;
height: 60px;
overflow: hidden;
border: 4px solid red;
border-radius: 5px;
}
.marquee-text-wrapper {
display: flex;
align-items: center;
height: 100%;
}
.marquee-text {
width: auto;
font-size: 20px;
white-space: nowrap;
/*color: red;*/
}
.marquee-text2 {
display: none;
margin-left: 200px;
/*color: blue;*/
}
.marquee-text-wrapper.moveFirst {
animation: moveFirst linear forwards;
animation-delay: 3s;
}
.marquee-text-wrapper.moveInfinite {
animation: moveInfinite linear infinite;
}
@keyframes moveFirst {
0% {
transform: translateX(0);
}
100% {
transform: translateX(var(--width));
}
}
@keyframes moveInfinite {
0% {
transform: translateX(0);
}
100% {
transform: translateX(var(--width));
}
}
</style>
</head>
<body>
<div class="marquee-wrapper">
<div class="marquee-text-wrapper">
<div class="marquee-text">
这是第一行横向滚动的提示文字信息1这是第一行横向滚动的提示文字信息2。
</div>
<div class="marquee-text marquee-text2">
这是第一行横向滚动的提示文字信息1这是第一行横向滚动的提示文字信息2。
</div>
</div>
</div>
<script>
const container = document.querySelector('.marquee-wrapper');
const marqueeTextWrapper = document.querySelector('.marquee-text-wrapper');
const marqueeText = document.querySelector('.marquee-text');
const containerWidth = container.offsetWidth
const marqueeTextWidth = marqueeText.offsetWidth;
let marqueeTextWrapperWidth = marqueeTextWidth
console.log(marqueeTextWidth > containerWidth)
if (marqueeTextWidth > containerWidth) {
document.querySelector('.marquee-text2').style.display = 'block';
// 重新计算滚动内容的宽度
marqueeTextWrapperWidth = marqueeTextWidth + 200;
const speed = 100; // 滚动速度,单位 px/s
const firstAnimationDuration = marqueeTextWrapperWidth / speed; // 第一次动画持续时间
document.documentElement.style.setProperty('--width', `-${marqueeTextWrapperWidth}px`);
marqueeTextWrapper.classList.add('moveFirst');
marqueeTextWrapper.style.animationDuration = `${firstAnimationDuration}s`;
// 注意⚠️:第一次动画执行不能是infinite,否则不会监听到动画结束
marqueeTextWrapper.addEventListener('animationend', () => {
marqueeTextWrapper.classList.remove('moveFirst');
marqueeTextWrapper.classList.add('moveInfinite');
marqueeTextWrapper.style.transform = 'translateX(0)';
})
}
</script>
</body>
</html>
- JS实现 ------ 使用**
window.requestAnimationFrame()
** 方法
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style lang="scss">
:root {
--width: 0;
}
.marquee-wrapper {
box-sizing: border-box;
position: absolute;
top: 50%;
left: 30%;
transform: translate(-20%, -50%);
display: flex;
align-items: center;
width: 600px;
height: 60px;
overflow: hidden;
border: 4px solid red;
border-radius: 5px;
}
.marquee-text-wrapper {
display: flex;
align-items: center;
height: 100%;
}
.marquee-text {
width: auto;
font-size: 20px;
white-space: nowrap;
}
.marquee-text2 {
display: none;
margin-left: 200px;
/*color: red;*/
}
</style>
</head>
<body>
<button onclick="handleStart()">开始</button>
<button onclick="handleEnd()">结束</button>
<div class="marquee-wrapper">
<div class="marquee-text-wrapper">
<div class="marquee-text">
这是第一行横向滚动的提示文字信息1这是第一行横向滚动的提示文字信息2。
</div>
<div class="marquee-text marquee-text2">
这是第一行横向滚动的提示文字信息1这是第一行横向滚动的提示文字信息2。
</div>
</div>
</div>
<script>
const marqueeWrapper = document.querySelector('.marquee-wrapper');
const marqueeTextWrapper = document.querySelector('.marquee-text-wrapper');
const marqueeText = document.querySelector('.marquee-text');
const marqueeWrapperWidth = marqueeWrapper.offsetWidth;
const marqueeTextWidth = marqueeText.offsetWidth;
let requestAnimationId = null;
let startTimeStamp = null;
// 判断是否溢出
if (marqueeTextWidth > marqueeWrapperWidth) {
document.querySelector('.marquee-text2').style.display = 'block';
// 指定开始位置 指定从容器右侧开始滚动出现
const startPosition = 600
// 重新计算滚动内容的宽度 (200: 第二个元素与第一个元素的 marginLeft 距离)
const offsetWidth = marqueeTextWidth + 200;
// 创建动画函数
animationObj = createAnimationFn(marqueeTextWrapper, startPosition, offsetWidth);
// 开始执行执行动画
requestAnimationId = animationObj.start();
} else {
marqueeTextWrapper.style.transform = `translateX(0)`;
}
/*
* 创建滚动动画
* el : 要滚动的元素
* startPosition: 从指定位置开始滚动
* offsetWidth: 滚动元素的总宽度
* */
function createAnimationFn(el, startPosition, offsetWidth) {
// 60HZ: 1s/60 (1000 / 60) * 0.06 = 16.666666666666668 * 0.06 = 1
// 这个速度是 1s移动 60px
const speed = 0.06;
let cancel = false;
const func = (timeStamp) => {
if (cancel) return
let moveX = 0
if (startTimeStamp) {
moveX = Math.round((timeStamp - startTimeStamp) * speed)
} else {
startTimeStamp = timeStamp
}
let x = Math.floor(startPosition - moveX)
if (x <= -offsetWidth) {
startTimeStamp = null;
startPosition = 0;
x = 0;
el.style.transform = `translateX(${x}px)`;
if (!cancel) {
requestAnimationId = window.requestAnimationFrame(func)
}
return
}
if (el && x > -offsetWidth) {
el.style.transform = `translateX(${x}px)`
if (!cancel) {
requestAnimationId = window.requestAnimationFrame(func)
}
}
}
return {
start: () => {
cancel = false
return window.requestAnimationFrame(func)
},
// 可以再加一些暂停函数等
end: (requestId) => {
cancel = true
window.cancelAnimationFrame(requestId)
},
}
}
function handleStart() {
if (!animationObj) return
startTimeStamp = null;
requestAnimationId = animationObj.start()
}
function handleEnd() {
if (!animationObj) return
animationObj.end(requestAnimationId)
}
</script>
</body>
</html>
window.requestAnimationFrame()