134 lines
3.4 KiB
Plaintext
134 lines
3.4 KiB
Plaintext
import { useState, useEffect, useRef } from 'react';
|
||
|
||
const AutoSwitchText = ({
|
||
texts = [],
|
||
interval = 2000,
|
||
autoPlay = true
|
||
}) => {
|
||
const [activeIndex, setActiveIndex] = useState(0);
|
||
const [isTransitioning, setIsTransitioning] = useState(false);
|
||
const timerRef = useRef(null);
|
||
const containerRef = useRef(null);
|
||
|
||
// 切换到下一个文案
|
||
const switchToNext = () => {
|
||
if (texts.length <= 1 || isTransitioning) return;
|
||
|
||
setIsTransitioning(true);
|
||
|
||
// 动画阶段1:缩小模糊
|
||
if (containerRef.current) {
|
||
containerRef.current.style.transform = 'scaleX(0.66)';
|
||
containerRef.current.style.filter = 'blur(4px)';
|
||
containerRef.current.style.opacity = '0.4';
|
||
}
|
||
|
||
// 等待缩小完成
|
||
setTimeout(() => {
|
||
// 切换索引
|
||
setActiveIndex((prev) => (prev + 1) % texts.length);
|
||
|
||
// 动画阶段2:展开清晰
|
||
setTimeout(() => {
|
||
if (containerRef.current) {
|
||
containerRef.current.style.transform = 'scaleX(1)';
|
||
containerRef.current.style.filter = 'blur(0)';
|
||
containerRef.current.style.opacity = '0.64';
|
||
}
|
||
|
||
// 动画完成
|
||
setTimeout(() => {
|
||
setIsTransitioning(false);
|
||
}, 200);
|
||
}, 100);
|
||
}, 400);
|
||
};
|
||
|
||
// 自动播放
|
||
useEffect(() => {
|
||
if (!autoPlay || texts.length <= 1) return;
|
||
|
||
const startInterval = () => {
|
||
timerRef.current = setInterval(switchToNext, interval);
|
||
};
|
||
|
||
startInterval();
|
||
|
||
return () => {
|
||
if (timerRef.current) {
|
||
clearInterval(timerRef.current);
|
||
}
|
||
};
|
||
}, [texts, interval, autoPlay]);
|
||
|
||
// 手动点击切换
|
||
const handleClick = () => {
|
||
if (timerRef.current) {
|
||
clearInterval(timerRef.current);
|
||
}
|
||
switchToNext();
|
||
|
||
// 重启自动播放
|
||
if (autoPlay) {
|
||
timerRef.current = setInterval(switchToNext, interval);
|
||
}
|
||
};
|
||
|
||
if (texts.length === 0) return null;
|
||
|
||
return (
|
||
<div
|
||
ref={containerRef}
|
||
className="move-item"
|
||
onClick={handleClick}
|
||
onMouseEnter={() => {
|
||
if (timerRef.current && autoPlay) {
|
||
clearInterval(timerRef.current);
|
||
}
|
||
}}
|
||
onMouseLeave={() => {
|
||
if (autoPlay && texts.length > 1) {
|
||
timerRef.current = setInterval(switchToNext, interval);
|
||
}
|
||
}}
|
||
style={{
|
||
cursor: 'pointer',
|
||
transition: 'all 0.4s cubic-bezier(0.4, 0, 0.2, 1)',
|
||
transform: 'scaleX(1)',
|
||
filter: 'blur(0)',
|
||
opacity: 0.64
|
||
}}
|
||
title={texts.length > 1 ? `点击切换,共${texts.length}个文案` : texts[0]}
|
||
>
|
||
{texts[activeIndex]}
|
||
|
||
{/* 小圆点指示器 */}
|
||
{texts.length > 1 && (
|
||
<div style={{
|
||
position: 'absolute',
|
||
bottom: '-15px',
|
||
left: '50%',
|
||
transform: 'translateX(-50%)',
|
||
display: 'flex',
|
||
gap: '4px'
|
||
}}>
|
||
{texts.map((_, index) => (
|
||
<div
|
||
key={index}
|
||
style={{
|
||
width: '4px',
|
||
height: '4px',
|
||
borderRadius: '50%',
|
||
backgroundColor: index === activeIndex ? '#37393d' : '#cccccc',
|
||
opacity: index === activeIndex ? 0.8 : 0.4,
|
||
transition: 'all 0.3s ease'
|
||
}}
|
||
/>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default AutoSwitchText; |