碰到个及其恶心的需求,需要文本溢出后水平滚动但是还要无缝;

辅助函数

/**
* @text:文本
* @size:字体大小
*/
export const getTextWidth = (text: string, size: number) => {
const span = document.createElement('span');
span.style.position = 'absolute'; // 避免影响页面布局
span.style.visibility = 'hidden'; // 隐藏元素
span.style.whiteSpace = 'nowrap'; // 防止换行
span.style.fontSize = size + 'px'; // 设置字体和大小
span.textContent = text;
document.body.appendChild(span);
const width = span.offsetWidth; // 或 span.getBoundingClientRect().width;
document.body.removeChild(span); // 移除元素
return width;
}

实现函数

import { getTextWidth } from "@/utils";

interface ScrollXAnimationOptions {
selector: string;
scrollSpeed?: number;
spacing?: number; // 添加间距选项
}

/**
* 实现文字溢出水平滚动动画
* @selector 选择器
* @scrollSpeed 滚动速度
*/
const useScrollXAnimation = ({ selector, scrollSpeed = 1 }: ScrollXAnimationOptions) => {
const animationIds = new Map<HTMLElement, number>();

const rs = document.querySelectorAll(selector) as NodeListOf<HTMLElement>;

rs.forEach((el) => {
// 复制内容并添加到元素尾部 以防止内容被截断 需要无缝滚动
const content = el.innerHTML;
const style = window.getComputedStyle(el);
const fontSize = style.getPropertyValue('font-size');
const size = fontSize.split('px')[0];
const textWidth = getTextWidth(el.textContent as string, +size);

// 添加间距元素 0.2 是无缝滚动的上个元素个下个的间距比例(元素大小*0.2) 可随意修改
el.innerHTML += `<span style="width:${textWidth * 0.2}px; display:inline-block;height:${size}px;"></span>${content}`;

let start = 0;
const elWidth = textWidth + (textWidth * 0.2);

const animate = () => {
start -= scrollSpeed;

if (start <= -elWidth) {
start = 0; // 重置到起始位置
}

el.style.transform = `translateX(${start}px)`;

animationIds.set(el, requestAnimationFrame(animate));
};

animate();

const handleMouseEnter = () => {
const id = animationIds.get(el);
if (id) {
cancelAnimationFrame(id);
animationIds.delete(el);
}
};

const handleMouseLeave = () => {
if (!animationIds.has(el)) {
animate();
}
};

el.addEventListener('mouseenter', handleMouseEnter);
el.addEventListener('mouseleave', handleMouseLeave);

return () => {
el.removeEventListener('mouseenter', handleMouseEnter);
el.removeEventListener('mouseleave', handleMouseLeave);

const id = animationIds.get(el);
if (id) {
cancelAnimationFrame(id);
animationIds.delete(el);
}
};
});

return () => {
animationIds.forEach((id) => cancelAnimationFrame(id));
animationIds.clear();
};
};

export default useScrollXAnimation;

使用方法如下

在合适的地方调用此hook(需要能访问到 .scroll-x-anmin)有此类名就会水平滚动;

import useScrollXAnimation from "@/hook/event/useTextScroll";

useScrollXAnimation({ selector: ".scroll-x-anmin", scrollSpeed: 1 });

//需要滚动的元素 如下意思是元素文字大小超過434會滾動

<div className="w-[435px] overflow-hidden relative mx-auto text-[13px]">
<div className={ getTextWidth(item?.title, 13)>434 ? "scroll-x-anmin" : "" } > {item.title}</div>
</div>