包含手动拖动,点击暂停,循环播放
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.container {
width: 300px;
border: solid 2px red;
overflow: hidden;
margin: 1em 0;
}
.marquee {
width: fit-content;
overflow: hidden;
position: relative;
display: flex;
}
.item {
padding: 2px 3px;
background-color: rgb(17, 49, 77);
margin: 0 5px;
color: azure;
font-size: 2em;
white-space: nowrap;
user-select: none;
}
.marquee-1 {
background-color: rgb(153, 82, 143);
}
</style>
</head>
<body>
<div class="container">
<div class="marquee">
<div class="item">1动画在每个循环中反向播放</div>
<div class="item">2换句话说,每次动画循环时</div>
<div class="item">3动画将重置为结束状态并重新开始</div>
</div>
</div>
<div class="container">
<div class="marquee marquee-1">
<div class="item">1动画在每个循环中反向播放</div>
<div class="item">2换句话说,每次动画循环时</div>
<div class="item">3动画将重置为结束状态并重新开始</div>
</div>
</div>
<script>
function getTranslateX(element) {
// 获取元素的transform样式
let transform = window.getComputedStyle(element).transform || "none";
// 检查transform是否为none,或者为空字符串
if (transform === "none" || transform === "") {
return 0;
}
// 正则表达式匹配translateX的值
// 注意:这个正则表达式假设translateX是transform属性中唯一或第一个出现的变换
// 如果translateX不是第一个,或者transform包含多个translateX,这个正则表达式可能需要调整
let matrixValues = transform
.match(/matrix.*\((.+?)\)/)?.[1]
.split(",")
.map(Number);
// 根据CSS变换矩阵,translateX的值是matrix的第四个元素(索引为3)
// 注意:如果transform包含的是translate3d(x, y, z),则translateX是第一个元素
// 这里我们假设只处理2D变换(matrix或translate/translateX)
if (matrixValues) {
// 对于matrix(a, b, c, d, e, f),translateX的值是e
// 但对于translateX(x),我们需要从字符串中直接提取x的值
// 这里我们假设transform属性只包含translateX或matrix
if (transform.startsWith("matrix")) {
return matrixValues[4] || 0; // 索引从0开始,所以translateX是索引4
} else if (transform.startsWith("translateX")) {
// 直接从transform字符串中提取translateX的值
// 假设transform是'translateX(10px)'这样的格式
let match = transform.match(/translateX\(([^)]+)\)/);
return match ? parseFloat(match[1]) : 0;
}
}
// 如果没有找到translateX,或者transform属性格式不符合预期,返回0
return 0;
}
const PxPerSecond = 100;
function marquee($marquee, from, to) {
const animate = $marquee.animate(
[
{
transform: `translateX(${from}px)`,
},
{
transform: `translateX(${to}px)`,
},
],
{
duration: (Math.abs(to - from) / PxPerSecond) * 1000,
fill: "forwards",
}
);
animate.addEventListener("finish", (e) => {
marquee($marquee, 0, to);
});
}
document.querySelectorAll(".container").forEach(($container) => {
const $marquee = $container.querySelector(".marquee");
const containerWidth = $container.clientWidth;
const marqueeWidth = $marquee.offsetWidth;
const $items = Array.from($marquee.querySelectorAll(".item"));
const $clones = $items.map(($item) => $item.cloneNode(true));
//首尾相接,如果不够长多加几次
$marquee.append(...$clones);
marquee($marquee, 0, -marqueeWidth);
let touchStartX = 0;
$marquee.addEventListener("touchstart", (e) => {
const currX = getTranslateX($marquee);
$marquee.getAnimations().forEach((animate) => {
animate?.cancel();
});
$marquee.style.transform = `translateX(${currX}px)`;
const [touch] = e.touches;
touchStartX = touch.clientX;
});
$marquee.addEventListener("touchend", (e) => {
const currX = getTranslateX($marquee);
marquee($marquee, currX, -marqueeWidth);
});
$marquee.addEventListener("touchmove", (e) => {
e.preventDefault();
const [touch] = e.touches;
const currX = getTranslateX($marquee);
let translateX = currX + touch.clientX - touchStartX;
// console.log(
// touch.clientX,
// touchStartX,
// currX,
// translateX,
// marqueeWidth
// );
if (translateX < -marqueeWidth) {
//最右边
translateX += marqueeWidth;
} else if (translateX >= 0) {
translateX -= marqueeWidth;
}
$marquee.style.transform = `translateX(${translateX}px)`;
touchStartX = touch.clientX;
});
});
</script>
</body>
</html>