基于React.js實(shí)現(xiàn)簡(jiǎn)單的文字跑馬燈效果
剛好手上有一個(gè)要實(shí)現(xiàn)文字跑馬燈的react項(xiàng)目,然后ant-design上面沒有這個(gè)組件,于是只能自己手?jǐn)]一個(gè)。
我想到的最簡(jiǎn)單的方法,就是定位啦,定時(shí)移動(dòng)這個(gè)文字塊不就跑起來了。
需要注意的是,要判斷文字的寬度,當(dāng)文字超出屏幕的寬度的時(shí)候再動(dòng)起來,我用的是hook的寫法,要在銷毀頁面的時(shí)候清掉定時(shí)器。定時(shí)器要放在useRef里面。
const timer = useRef<any>(null);
const [left, setLeft] = useState(0);
const contentRef = useRef<any>(null);
useEffect(() => {
// 當(dāng)監(jiān)聽到文字變化時(shí),一定要先清掉定時(shí)器,如果文字較短的話就不會(huì)再啟動(dòng)
if (timer.current) { clearInterval(timer.current); }
const contentDom = contentRef.current;
if (contentDom) {
const obj = contentDom.getBoundingClientRect();
// 判斷文字框長(zhǎng)度
if (obj.width > props.width) {
timer.current = setInterval(() => {
// 注意state是負(fù)數(shù),?當(dāng)數(shù)字移動(dòng)到最后的時(shí)候,下一次從父元素的寬度開始,看起來就是一直在向左移動(dòng)
// 文字框的寬度要時(shí)時(shí)獲取
// setLeft要從回調(diào)里面獲取,不然不能時(shí)時(shí)更新
setLeft((state) =>-state >= contentDom.getBoundingClientRect().width ? props.width : state - 1, ); }, 100);
} else { setLeft(0); }
}
}, [props.children]);
useEffect(() => {
// 注銷時(shí),清空定時(shí)器
return () => {
if (timer.current) { clearInterval(timer.current); }
};
}, []);
return (<div className={styles.noticeCompWrapper} style={{ width: props.width, ...props.style }}>
<div ref={contentRef} className={styles.noticeContent} style={{ left }}> {props.children} </div>
</div> );
.noticeCompWrapper {
height: 40px;
line-height: 40px;
overflow: hidden;
position: relative;
.noticeContent {
white-space: nowrap;
position: absolute;
}
}這移動(dòng)效果還可以吧,時(shí)間間隔一定要小,不然就會(huì)一卡一卡的

第一種很容易吧,其實(shí)最開始是想用純css來寫的,考慮到css沒法獲取自適應(yīng)寬度,咋判斷文字移動(dòng)到末尾了?但是我覺得,css肯定是可以辦到,于是我繼續(xù)探索...
animation走起,,,咱們假設(shè)外邊框長(zhǎng)120px,文字長(zhǎng)240px
@keyframes run {
0% { transform: translateX(0); }
50% { transform: translateX(-240px); }
50% { transform: translateX(120px); }
100% { transform: translateX(-240px); }
}總感覺有啥不對(duì),這個(gè)字咋往回跑,這感覺跑來跑去的。。。

平心靜氣~沒事沒事,不就是個(gè)小bug么~
仔細(xì)思考一下,這只要寫兩個(gè)動(dòng)畫就解決了,因?yàn)槠鋵?shí)除了第一次不同,后面都是從右邊進(jìn)入視角的有木有。
@keyframes run {
from { transform: translateX(0); }
to { transform: translateX(-240px); }
}
@keyframes loop {
from { transform: translateX(120px); }
to { transform: translateX(-240px); }
}咱們用的時(shí)候,第一個(gè)走一遍就好了,循環(huán)后面那個(gè):
.textContent {
white-space: nowrap;
animation-name: run, loop;
animation-duration: 5s;
animation-iteration-count: 1, infinite;
animation-timing-function: linear;
position: relative;
}look,是不是好多了~

接下來就是文字長(zhǎng)度的問題了~咋們咋控制他要不要?jiǎng)影。窟€有移動(dòng)的時(shí)間和距離咋控制??
首先動(dòng)畫時(shí)間,less肯定是算不出來了,那我們就在js外面計(jì)算一下哈~方法和上面類似,要取元素的寬度。
const contentRef = useRef<any>(null);
const [duration, setDuration] = useState('');
useEffect(() => {
const dom = contentRef.current;
if (dom) {
const { width } = dom.getBoundingClientRect();
if (width > props.width) {
// 我這邊取的速度是按一個(gè)字的大小
setDuration(width / 16 + 's');
} else {
// 小于寬度的時(shí)候清掉時(shí)間
setDuration('');
}
} else { setDuration(''); }
}, [props.children]);
return (<div style={{ width: props.width, ...props.style }} className={styles.wrapper} >
<div
className={styles.textContent}
ref={contentRef}
// 計(jì)算好動(dòng)畫時(shí)間傳過去
style={{
animationDuration: duration,
// 第二個(gè)動(dòng)畫等第一個(gè)執(zhí)行之后執(zhí)行
animationDelay: duration? `0s, ${duration}` :'',
}}
>
{props.children}
</div>
</div> );完整的樣式
// 設(shè)置父元素的寬度
@width: 120px;
.wrapper {
position: relative;
overflow: hidden;
width: @width;
height: 40px;
line-height: 40px;
.textContent {
white-space: nowrap;
animation-name: run, roop;
animation-iteration-count: 1, infinite;
animation-timing-function: linear;
// 這個(gè)很重要,不然寬度就和父元素一樣
position: absolute;
}
}
@keyframes run {
from { transform: translateX(0); }
to {
// 這個(gè)100% 是文字的
transform: translateX(-100%);
}
}
@keyframes roop {
from { transform: translateX(@width); }
to { transform: translateX(-100%); }
}不錯(cuò)不錯(cuò),這樣就解決了時(shí)間和制動(dòng)的問題了~
不過...,這還不夠完美。NOT PERFACT!
這個(gè)父元素的寬度是不是寫死了,要是要使用的話只能手動(dòng)改@width,咱有木有辦法通過js傳過來?你知道怎么改更好么?
哈哈,咱們基本上已經(jīng)完成了這種簡(jiǎn)單的從左向右移動(dòng)的文字跑馬燈(為自己鼓掌),接下來就是升級(jí)版的了,跑馬燈plus。實(shí)現(xiàn)一個(gè)向上滾動(dòng)的跑馬燈。
咱們?cè)诘谝徊降幕A(chǔ)上來做一個(gè)向上滾動(dòng)的跑馬燈plus。
我們加一個(gè)向上的按鈕,可以控制文字跑動(dòng)的方向,當(dāng)然向右向下同理~這里就不做了
<>
<div
className={styles.noticeCompWrapper}
style={{ width: props.width, ...props.style, marginBottom: 10 }}
>
<div
ref={contentRef}
className={styles.noticeContent}
style={{ left, top,
// 換行的邏輯一定要加上,上下移動(dòng)的需要換行
whiteSpace: direction == DirectionEnum.左 ? 'nowrap' : 'initial',
}}
>
{props.children}
</div>
</div>
<Space>
<Button onClick={() => { setDirection(DirectionEnum.左); setTop(0); }}> 向左</Button>
<Button onClick={() => { setDirection(DirectionEnum.上); setLeft(0); }}> 向上</Button>
</Space>
</>判斷下移動(dòng)方向
useEffect(() => {
// 當(dāng)監(jiān)聽到文字變化時(shí),一定要先清掉定時(shí)器,如果文字較短的話就不會(huì)再啟動(dòng)
if (timer.current) { clearInterval(timer.current); }
const contentDom = contentRef.current;
if (contentDom) {
const obj = contentDom.getBoundingClientRect();
if (direction == DirectionEnum.左) {
// 同上...
} else if (direction == DirectionEnum.上) {
// 向上
if (obj.height > 40) {
timer.current = setInterval(() => {
setTop(state =>-state >= contentDom.getBoundingClientRect().height ? 40 : state - 1);
}, 30);
}
} else {
setLeft(0);
setTop(0);
}
}
}, [props.children, direction]);
看一下成果:

這種單行滾動(dòng)的效果,能不能實(shí)現(xiàn)一下?就是滾動(dòng)一行停留一段時(shí)間再繼續(xù)滾動(dòng)

這個(gè)也比較簡(jiǎn)單,我用我上面的方法在引申一下,你們也可以用其他方法,animatioin也可以。
我在定時(shí)器里面在加一個(gè)延時(shí)timeout
timer.current = setInterval(() => {
setTop(state => {
// 當(dāng)行數(shù)是40的整倍數(shù)的時(shí)候延遲執(zhí)行移動(dòng)
if ((state - 1) % 40 == 0) {
setTimeout(() => {
setTop(state1 => -state1 >= contentDom.getBoundingClientRect().height ? 40 : state1 - 1);
}, 500);
return state;
} else {
return -state >= contentDom.getBoundingClientRect().height? 40 : state - 1;
}
});
}, 30);效果:

以上就是基于React.js實(shí)現(xiàn)簡(jiǎn)單的文字跑馬燈效果的詳細(xì)內(nèi)容,更多關(guān)于React文字跑馬燈的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Hello?React的組件化方式之React入門小案例演示
這篇文章主要介紹了Hello?React的組件化方式-React入門小案例,本文通過Hello?React的案例,?來體驗(yàn)一下React開發(fā)模式,?以及jsx的語法,需要的朋友可以參考下2022-10-10
react-native-fs實(shí)現(xiàn)文件下載、文本存儲(chǔ)的示例代碼
本篇文章主要介紹了react-native-fs實(shí)現(xiàn)文件下載、文本存儲(chǔ)的示例代碼,具有一定的參考價(jià)值,有興趣的可以了解下2017-09-09
ReactNative踩坑之配置調(diào)試端口的解決方法
本篇文章主要介紹了ReactNative踩坑之配置調(diào)試端口的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
解決配置setupProxy.js代理,頁面報(bào)錯(cuò)404問題
這篇文章主要介紹了解決配置setupProxy.js代理,頁面報(bào)錯(cuò)404問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
useReducer?createContext代替Redux原理示例解析
這篇文章主要為大家介紹了useReducer?createContext代替Redux原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
React純前端模擬實(shí)現(xiàn)登錄鑒權(quán)
這篇文章主要為大家詳細(xì)介紹了React純前端模擬實(shí)現(xiàn)登錄鑒權(quán)的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-04-04
使用react-activation實(shí)現(xiàn)keepAlive支持返回傳參
本文主要介紹了使用react-activation實(shí)現(xiàn)keepAlive支持返回傳參,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
深入理解React調(diào)度(Scheduler)原理
本文主要介紹了深入理解React調(diào)度(Scheduler)原理,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
解決React報(bào)錯(cuò)Property?'value'?does?not?exist?on?
這篇文章主要為大家介紹了React報(bào)錯(cuò)Property?'value'?does?not?exist?on?type?EventTarget的解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12

