1. 程式人生 > 程式設計 >js實現一個逐步遞增的數字動畫

js實現一個逐步遞增的數字動畫

目錄
  • 背景
  • 實現類似滾輪的效果,容器固定,數字向上滾動
  • 利用兩個元素實現滾動
  • 利用H5的requestAnimationFrame()API實現數字逐步遞增的動畫效果
  • 計時器對比
  • requestAnimationFrame實現滾動動畫思路
  • 成果展示

背景

視覺化大屏專案使用最多的元件就是數字元件,展示資料的一個變化,為了提高視覺效果,需要給數字增加一個滾動效果,實現一個數字到另一個數字逐步遞增的滾動動畫。

先上一個思維導圖:

js實現一個逐步遞增的數字動畫

實現類似滾輪的效果,容器固定,數字向上滾動

js實現一個逐步遞增的數字動畫

先列舉所有的可能的值形成一個縱向的列表http://www.cppcns.com,然後固定一個容器,勻速更改數字的偏移值。

下面來介紹一下這種方案的實現,元素值從0到9一共十http://www.cppcns.com

個值,每個數字佔縱向列表的10%,所以縱向偏移值依次為為0% —> -90%

實現:

<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,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)

上述程式碼存在一個問題,當我們從9到0的時候,容器偏移從-90%直接到了0%。 但是由於設定了固定的過渡動畫時間,就會出現一個向反方向滾動的情況,為了解決這個問題,可以參考無縫滾動的思路

  • 在9後面複製一份0,
  • 當縱向列表滾動到9的時候,繼續滾動到複製的0
  • 滾動到複製的0的時候,把列表的偏移位置改為0,並且控制動畫時間為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}%)`
}

js實現一個逐步遞增的數字動畫

利用兩個元素實現滾動

仔細看動圖的效果,事實上在在視口只有兩個元素,一個值之前的值,一個為當前的值,滾動偏移值只需設定translateY(-100%)

具體思路:

  • 宣告兩個變數,分別存放之前的值prev,以及變化後的值cur;宣告一個變數play作為這兩個值的滾動動畫的開關
  • 使用useEffect監聽監聽傳入的值:如果是有效的數字,那麼把沒有變化前的值賦值給prev,把當前傳入的值賦值給cur,並且設定paly為true開啟滾動動畫

下面是調整後的程式碼結構:

 <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;
}

實現多個滾輪的向上滾動的數字元件

js實現一個逐步遞增的數字動畫

利用H5的requestAnimationFrame()API實現數字逐步遞增的動畫效果

實現一個數字的逐漸遞增的滾動動畫,並且要在指定時間內完成。要看到流暢的動畫效果,就需要在更新元素狀態時以一定的頻率進行,動畫都是通過在很短的時間內不停的渲染/繪製元素做到的,所以計時器一直都是動畫的核心技術,關鍵就是重新整理的間隔時間,重新整理時間需要儘量短,這樣動畫效果才能顯得更加流暢,不卡頓;同時重新整理間隔又不能太短,需要確保瀏覽器有能力渲染動畫 。
大多數電腦顯示器的重新整理頻率是 60Hz,即每秒重繪 60次。因此平滑動畫的最佳迴圈間隔是通常是 1000ms/60,約等於16.6ms

計時器對比

  • 與 setTimeout 和 setInterval 不同,requestAnimationFrawww.cppcns.comme 不需要程式設計師自己設定時間間隔。setTimeout 和 setInterval 的問題是精確度低。它們的內在執行機制決定了時間間隔引數實際上只是指定了把動畫程式碼新增到瀏覽器 UI 執行緒佇列中以等待執行的時間。如果佇列前面已經加入了其他任務,那動畫程式碼就要等前面的任務完成後再執行。
  • requestAnimationFrame 採用系統時間間隔,它會要求瀏覽器根據自己的頻率進行一次重繪,保持最佳繪製效率,不會因為間隔時間過短,造成過度繪製,增加開銷;也不會因為間隔時間太長,使用動畫卡頓不流暢,讓各種動畫效果能夠有一個統一的重新整理機制,從而節省系統資源,提高系統性能,改善視覺效果。
  • requestAnimationFrame 會把每一幀中的所有 DOM 操作集中起來,在一次重繪或迴流中就完成,並且重繪或迴流的時間間隔緊緊跟隨瀏覽器的重新整理頻率。
  • requestAnimationFrame 對於隱藏或不可見元素,將不會進行重繪或迴流,就意味著使用更少的 CPU、GPU 和記憶體使用量。
  • requestAnimationFrame 是由瀏覽器專門為動畫提供的API,在執行時瀏覽器會自動優化方法的呼叫,並且如果頁面不是啟用狀態下的話,動畫會自動暫停,有效節省了CPU開銷。

requestAnimatnXdBJmqrswionFrame實現滾動動畫思路

動畫開始,記錄開始動畫的時間 startTimeRef.current

const startTimeRef = useRef(Date.now());
const [t,setT] = useState(Date.now());

之後每一幀動畫,記錄從開始動畫經過了多長時間,計算出當前幀的所到達的數字應該是多少,即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]);

針對當前每個數字位上的數字進行比較,如果有變化,進行偏移量的變化,偏移量體現在當前數字位上的數字與下一位數字之間的差值,這個變化每一幀都串起來形成了滾動動畫

成果展示

js實現一個逐步遞增的數字動畫

js實現一個逐步遞增的數字動畫

到此這篇關於js實現一個逐步遞增的數字動畫的文章就介紹到這了,更多相關js 逐步遞增數字動畫內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!