js實(shí)現(xiàn)一個(gè)逐步遞增的數(shù)字動(dòng)畫
背景
可視化大屏項(xiàng)目使用最多的組件就是數(shù)字組件,展示數(shù)據(jù)的一個(gè)變化,為了提高視覺(jué)效果,需要給數(shù)字增加一個(gè)滾動(dòng)效果,實(shí)現(xiàn)一個(gè)數(shù)字到另一個(gè)數(shù)字逐步遞增的滾動(dòng)動(dòng)畫。
先上一個(gè)思維導(dǎo)圖:

實(shí)現(xiàn)類似滾輪的效果,容器固定,數(shù)字向上滾動(dòng)

先列舉所有的可能的值形成一個(gè)縱向的列表,然后固定一個(gè)容器,勻速更改數(shù)字的偏移值。
下面來(lái)介紹一下這種方案的實(shí)現(xiàn),元素值從0到9一共十個(gè)值,每個(gè)數(shù)字占縱向列表的10%,所以縱向偏移值依次為為0%? —> -90%
實(shí)現(xiàn):
<ul>
<li>
<span>0123456789</span>
</li>
</ul>
ul{
margin-top: 200px;
}
ul li{
margin:0 auto;
width: 20px;
height: 30px;
text-align: center;
border:2px solid rgba(221,221,221,1);
border-radius:4px;
}
ul li span{
position: absolute;
color: #fff;
top: 30%;
left: 50%;
transform: translate(-50%,0);
transition: transform 500ms ease-in-out;
writing-mode: vertical-rl;
text-orientation: upright;
letter-spacing: 17px;
}
let spanDom = document.querySelector('span')
let start = 0
setInterval(() =>{
start++
if(start>9){
start = 0
}
spanDom.style.transform = `translate(-50%,-${start*10}%)`
}, 1000)
上述代碼存在一個(gè)問(wèn)題,當(dāng)我們從9到0的時(shí)候,容器偏移從-90%直接到了0%。 但是由于設(shè)定了固定的過(guò)渡動(dòng)畫時(shí)間,就會(huì)出現(xiàn)一個(gè)向反方向滾動(dòng)的情況,為了解決這個(gè)問(wèn)題,可以參考無(wú)縫滾動(dòng)的思路
- 在9后面復(fù)制一份0,
- 當(dāng)縱向列表滾動(dòng)到9的時(shí)候,繼續(xù)滾動(dòng)到復(fù)制的0
- 滾動(dòng)到復(fù)制的0的時(shí)候,把列表的偏移位置改為0,并且控制動(dòng)畫時(shí)間為0
<ul>
<li>
<span>01234567890</span>
</li>
</ul>
let spanDom = document.querySelector('span')
let start = 0
var timer = setInterval(fn, 1000);
function fn() {
start++
clearInterval(timer)
timer = setInterval(fn,start >10 ? 0 : 1000);
if(start>10){
spanDom.style.transition = `none`
start = 0
}else{
spanDom.style.transition = `transform 500ms ease-in-out`
}
spanDom.style.transform = `translate(-50%,-${start/11*100}%)`
}

利用兩個(gè)元素實(shí)現(xiàn)滾動(dòng)
仔細(xì)看動(dòng)圖的效果,事實(shí)上在在視口只有兩個(gè)元素,一個(gè)值之前的值,一個(gè)為當(dāng)前的值,滾動(dòng)偏移值只需設(shè)置translateY(-100%)
具體思路:
- 聲明兩個(gè)變量,分別存放之前的值prev,以及變化后的值cur;聲明一個(gè)變量play作為這兩個(gè)值的滾動(dòng)動(dòng)畫的開(kāi)關(guān)
- 使用useEffect監(jiān)聽(tīng)監(jiān)聽(tīng)傳入的值:如果是有效的數(shù)字,那么把沒(méi)有變化前的值賦值給prev,把當(dāng)前傳入的值賦值給cur,并且設(shè)置paly為true開(kāi)啟滾動(dòng)動(dòng)畫
下面是調(diào)整后的代碼結(jié)構(gòu):
<div className={styles.slider}>
{[prev, cur].map((item, index) => (
<span key={index} className={`${styles['slider-text']} ${playing && styles['slider-ani']} ${(prev === 0 && cur === 0 && index ===0) && styles['slider-hide']}`}>
{item}
</span>
))}
</div>
const { value} = props
const [prev, setPrev] = useState(0)
const [cur, setCur] = useState(0)
const [playing, setPlaying] = useState(false)
const play = (pre, current) => {
setPrev(pre)
setCur(current)
setPlaying(false)
setTimeout(() => {
setPlaying(true)
}, 20)
}
useEffect(() => {
if (!Number.isNaN(value)) {
play(cur, value)
} else {
setPrev(value)
setCur(value)
}
}, [value])
.slider {
display: flex;
flex-direction: column;
height: 36px;
margin-top: 24%;
overflow: hidden;
text-align: left;
}
.slider-text {
display: block;
height: 100%;
transform: translateY(0%);
}
.slider-ani {
transform: translateY(-100%);
transition: transform 1s ease;
}
.slider-hide {
opacity: 0;
}
實(shí)現(xiàn)多個(gè)滾輪的向上滾動(dòng)的數(shù)字組件

利用H5的requestAnimationFrame()API實(shí)現(xiàn)數(shù)字逐步遞增的動(dòng)畫效果
實(shí)現(xiàn)一個(gè)數(shù)字的逐漸遞增的滾動(dòng)動(dòng)畫,并且要在指定時(shí)間內(nèi)完成。要看到流暢的動(dòng)畫效果,就需要在更新元素狀態(tài)時(shí)以一定的頻率進(jìn)行,JS動(dòng)畫都是通過(guò)在很短的時(shí)間內(nèi)不停的渲染/繪制元素做到的,所以計(jì)時(shí)器一直都是Javascript動(dòng)畫的核心技術(shù),關(guān)鍵就是刷新的間隔時(shí)間,刷新時(shí)間需要盡量短,這樣動(dòng)畫效果才能顯得更加流暢,不卡頓;同時(shí)刷新間隔又不能太短,需要確保瀏覽器有能力渲染動(dòng)畫 。
大多數(shù)電腦顯示器的刷新頻率是 60Hz,即每秒重繪 60次。因此平滑動(dòng)畫的最佳循環(huán)間隔是通常是 1000ms/60,約等于16.6ms
計(jì)時(shí)器對(duì)比
- 與 setTimeout 和 setInterval 不同,requestAnimationFrame 不需要程序員自己設(shè)置時(shí)間間隔。setTimeout 和 setInterval 的問(wèn)題是精確度低。它們的內(nèi)在運(yùn)行機(jī)制決定了時(shí)間間隔參數(shù)實(shí)際上只是指定了把動(dòng)畫代碼添加到瀏覽器 UI 線程隊(duì)列中以等待執(zhí)行的時(shí)間。如果隊(duì)列前面已經(jīng)加入了其他任務(wù),那動(dòng)畫代碼就要等前面的任務(wù)完成后再執(zhí)行。
- requestAnimationFrame 采用系統(tǒng)時(shí)間間隔,它會(huì)要求瀏覽器根據(jù)自己的頻率進(jìn)行一次重繪,保持最佳繪制效率,不會(huì)因?yàn)殚g隔時(shí)間過(guò)短,造成過(guò)度繪制,增加開(kāi)銷;也不會(huì)因?yàn)殚g隔時(shí)間太長(zhǎng),使用動(dòng)畫卡頓不流暢,讓各種網(wǎng)頁(yè)動(dòng)畫效果能夠有一個(gè)統(tǒng)一的刷新機(jī)制,從而節(jié)省系統(tǒng)資源,提高系統(tǒng)性能,改善視覺(jué)效果。
- requestAnimationFrame 會(huì)把每一幀中的所有 DOM 操作集中起來(lái),在一次重繪或回流中就完成,并且重繪或回流的時(shí)間間隔緊緊跟隨瀏覽器的刷新頻率。
- requestAnimationFrame 對(duì)于隱藏或不可見(jiàn)元素,將不會(huì)進(jìn)行重繪或回流,就意味著使用更少的 CPU、GPU 和內(nèi)存使用量。
- requestAnimationFrame 是由瀏覽器專門為動(dòng)畫提供的API,在運(yùn)行時(shí)瀏覽器會(huì)自動(dòng)優(yōu)化方法的調(diào)用,并且如果頁(yè)面不是激活狀態(tài)下的話,動(dòng)畫會(huì)自動(dòng)暫停,有效節(jié)省了CPU開(kāi)銷。
requestAnimationFrame實(shí)現(xiàn)滾動(dòng)動(dòng)畫思路
動(dòng)畫開(kāi)始,記錄開(kāi)始動(dòng)畫的時(shí)間 startTimeRef.current
const startTimeRef = useRef(Date.now()); const [t, setT] = useState(Date.now());
之后每一幀動(dòng)畫,記錄從開(kāi)始動(dòng)畫經(jīng)過(guò)了多長(zhǎng)時(shí)間,計(jì)算出當(dāng)前幀的所到達(dá)的數(shù)字應(yīng)該是多少,即currentValue
useEffect(() => {
const rafFunc = () => {
const now = Date.now();
const t = now - startTimeRef.current;
if (t >= period) {
setT(period);
} else {
setT(t);
requestAnimationFrame(rafFunc);
}
};
let raf;
if (autoScroll) {
raf = requestAnimationFrame(rafFunc);
startTimeRef.current = Date.now();
} else {
raf && cancelAnimationFrame(raf);
}
return () => raf && cancelAnimationFrame(raf);
}, [period, autoScroll]);
const currentValue = useMemo(() => ((to - from) / period) * t + from, [t, period, from, to]);
針對(duì)當(dāng)前每個(gè)數(shù)字位上的數(shù)字進(jìn)行比較,如果有變化,進(jìn)行偏移量的變化,偏移量體現(xiàn)在當(dāng)前數(shù)字位上的數(shù)字與下一位數(shù)字之間的差值,這個(gè)變化每一幀都串起來(lái)形成了滾動(dòng)動(dòng)畫
成果展示


到此這篇關(guān)于js實(shí)現(xiàn)一個(gè)逐步遞增的數(shù)字動(dòng)畫的文章就介紹到這了,更多相關(guān)js 逐步遞增數(shù)字動(dòng)畫內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS關(guān)于for循環(huán)中使用setTimeout的四種解決方案
這篇文章主要介紹了JS關(guān)于for循環(huán)中使用setTimeout的四種解決方案,想深入了解JS的同學(xué),一定要看下2021-05-05
javascript使用location.search的示例
本文介紹javascript 使用location.search獲取當(dāng)前地址欄參數(shù)的實(shí)例2013-11-11
基于 webpack2 實(shí)現(xiàn)的多入口項(xiàng)目腳手架詳解
這篇文章主要給大家介紹了基于 webpack2 實(shí)現(xiàn)的多入口項(xiàng)目腳手架的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-06-06
JavaScript數(shù)據(jù)結(jié)構(gòu)與算法之隊(duì)列原理與用法實(shí)例詳解
這篇文章主要介紹了JavaScript數(shù)據(jù)結(jié)構(gòu)與算法之隊(duì)列原理與用法,較為詳細(xì)的說(shuō)明了隊(duì)列的概念、原理,并結(jié)合實(shí)例形式分析了javascript實(shí)現(xiàn)與使用隊(duì)列的相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下2017-11-11
解析ScrollPic在ie8下只滾動(dòng)一遍,然后變?yōu)榭瞻?ie6,ie7,chrome,firefox正常
解析ScrollPic在ie8下只滾動(dòng)一遍,然后變?yōu)榭瞻?ie6,ie7,chrome,firefox都正常)2013-06-06
JS實(shí)現(xiàn)對(duì)JSON數(shù)據(jù)進(jìn)行冒泡排序
JavaScript 是一種廣泛使用的腳本語(yǔ)言,JSON是一種常見(jiàn)的數(shù)據(jù)格式,這篇文章主要來(lái)探討一下如何使用 JavaScript 對(duì) JSON 數(shù)據(jù)進(jìn)行冒泡排序,感興趣的可以了解一下2023-06-06

