国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

React Motion 緩動函數剖析

wfc_666 / 2812人閱讀

摘要:大家可以嘗試使用的,配置一個合適的勁度系數和空氣阻力。所做的事,只不過自己實現了一套緩動函數。

根據經典力學的觀點,世界上所有的原子每時每刻仿佛都會根據當前速度、受力和位置計算出下一刻的速度、受力和位置。上帝有一臺超級計算機嗎?非也,反而計算機是我們利用原子的這些特性拼裝出來的。現在,我們卻要用計算機,像上帝那樣,再造一個世界。

我不知道這個世界上有沒有“仿世學”,但是既然動畫是要模仿現實世界,那么實現動畫的根本方法就是借鑒上帝的辦法——模擬自然規律。本文以 React Motion 實現原理為背景,介紹一種通用的模擬物理規律的方法,以及如何使用這種方法實現 React Motion 的緩動函數。讓我們來當一回上帝吧。

什么是緩動函數

動畫的原理看似復雜,其實就是每幀不停得渲染。一張靜態頁面的渲染就是在一幀中渲染。如何渲染每一幀呢?我們可以用最簡單,同時也是最繁瑣的方法,就像最原始的動畫片那樣,寫 n 張靜態頁面,然后每隔一幀切換一張。

假如我們已經勤奮地寫好了 P_1, P_2, ... P_n 這 n 張頁面,我們用它來實現一個簡單的動畫:

// pages: [P1, P2, P3 ... Pn];
const pageCount = pages.length;

const startAnimation = (currPageIndex) => {
  if (currIndex === pageCount) { return ; }
 
  document.body.innerHTML = (pages[currPageIndex++]);

  setTimeout(startAnimation.bind(null, currPageIndex), frameTime)
}

startAnimation(0);

用這種方法有著顯而易見的問題:

寫 n 張頁面頁面渲染效率十分低下。

每次重新設置 body.innerHTML,性能太低了。

我們來逐個解決上述問題。

每一幀的界面都遵循一定的規律,相似性很高,中間必然有很多重復勞動。既然是重復勞動,我們可以放心的交給計算機去完成。寫一個渲染函數,只需要向這個函數描述一下當前頁面的信息,這個函數就能把頁面給渲染出來。

可以用局部更新的方式來取代塊更新,其中 React 的 Virtual DOM 更新方便地解決了這個問題。

我們再以一個左右切換的 toggle 動畫為例,寫一個渲染函數:

const render = x => `
  
`

有了這個函數之后,只需要告訴它 x 的當前值,新的頁面就開始自動繪制了。由于 toggle 的運動規律,x 的值也不用手動依次給出,我們仍然可以寫一個自動計算 x 的函數。這個自動計算 x 的函數,或者說計算頁面狀態的函數,就是緩動函數。

假設這個 toggle 是勻速運動的,緩動函數便可以寫成這樣:

$$ distance(總路程) = endX - beginX $$

$$ v = frac{distance}{duration(總時間)} $$

$$ x = v cdot t + beginX $$

用代碼來表示,

const cal = (beginX, endX, duration, beginTime) => {
    const now = performance.now();
    const passedTime = now - beginTime;

    return (endX - beginX) / duration * passedTime + beginX;
}

最后完成這個 toggle 動畫:

const beginX = 0;
const endX = 300;
const duration = 5000;
const frameTime = 1000 / 60;
    
let beginTime = performance.now();

const startAnimation = () => {
  const currX = cal(beginX, endX, duration, beginTime);

  document.body.innerHTML = render(currX);

  setTimeout(startAnimation, frameTime);
}

startAnimation(0);
requestAnimationFrame (raf)

可以看到,上述章節使用 setTimeout 來模擬時間的逝去,然而瀏覽器為動畫過程提供了一個更為專注的 API - requestAnimationFrame

const update = now => {
  // calculate new state...

  // rerender here...

  raf(update);
};

raf(update);

raf 使用起來就像 setTimeout 一樣,但有以下優點:

所有注冊到 raf 中的回調,瀏覽器會統一管理, 在適當的時候一同執行所有回調。

當頁面不可見,例如當前標簽頁被切換,隱藏在后面的時候,為了減少終端的損耗,raf 就會暫停。(如果像 jQuery 那樣, 使用 setTimeout 實現動畫,此時頁面就會進行沒有意義的重繪)。

raf 的這個特性,還可以利用在實時模塊中,讓標簽頁隱藏時停止發請求。

在開始使用 raf 前,我們需要一個 raf 的 polyfill ,比如 chrisdickinson/raf

然后,我們嘗試用 React 和 raf 來重構一次 Toggle 動畫。在數據上,用中介者模式實現一個簡單的單向數據流:

const createStoreX = initialX => {
  let currX = initialX;
  let listeners = [];

  return {
    getX: () => currX,

    subscribe: listener => {
      listeners = [...listeners, listener];
    },

    changeX: newX => {
      currX = newX;
      listeners.forEach(listener);
    }
  };
}

const finalCreateStoreX = (createStoreX => initialX => {
  const store = createStoreX(initialX);

  return {
    ...store,
    changeX: newX => {
      store.changeX(newX);
    }
  };
})(createStoreX);

const store = finalCreateStoreX(0);

const View = x => (
  
); class Page extends React.Component { handleChangeX = () => { this.setState({ x: storeX.getX() }) } componentDidMount = () => { storeX.subscribe(this.handleChangeX) } render = () => } const startAnimation = (beginPos = 0, endPos = 300, duration = 5000, frameTime = 17) => { const now = performance.now(); const loop = () => { const passedTime = performance.now() - now; const distance = endPos - beginPos; const currX = distance/duration*passedTime + beginPos; storeX.changeX(currX); } setTimeout(loop, frameTime); }; reactDOM.render(, document.body)

有沒有覺得很棒!但仍然有優化的空間。動畫是源自現實世界的,人類早已習慣了一個變速運動的物理環境,這樣的一個勻速動畫會讓人相對感覺不適。為了優化用戶體驗,React Motion 使用了一種常見的變速運動 —— 彈簧運動。

React Motion 緩動原理剖析

React Motion 使動畫看起來像一個彈簧那樣(一個有空氣阻力的彈簧,如果沒有空氣阻力,彈簧就會不停地做簡諧運動了)。大家可以嘗試使用 React Motion 的spring-parameters-chooser,配置一個合適的勁度系數和空氣阻力。彈簧動畫可以使網站增添一些俏皮的元素,讓用戶體驗起來更加舒暢!

下面就讓我們進入主題,開始解讀 React Motion 的緩動過程。

先模擬彈簧的物理規律,實現彈簧動畫。

假設有一個彈簧,彈簧上綁了一個砝碼,回到初中物理,根據胡克定律,砝碼的受到彈簧的拉力為:

$$ F_{spring} = kvarDelta{x} (k為彈簧的勁度系數)$$

我們假設該砝碼受到的空氣阻力 kdamping 與砝碼當前的速度 vt 呈正相關,其中阻尼系數為 kdamping

對砝碼進行受力分析得:

$$ F = F_{spring} - F_{damping} = k_{spring}varDelta{x} - k_{damping} imes v_{t} $$

設 at 為砝碼當前加速度,得:

$$ F = ma_t $$

設 v" 和 x" 分別為經過 $$ dt $$ 時間后,砝碼新的速度和位移,得:

$$ a_t = lim_{dt o 0} frac{dv}{dt} = lim_{dt o 0} frac{v^{"} - v_t}{dt} $$

$$ v_t = lim_{dt o 0} frac{dx}{dt} = lim_{dt o 0} frac{x^{"} - x_t}{dt} $$

即:

$$ v^{"} = lim_{dt o 0} a_t*d_t + v_t $$

$$ x^{"} = lim_{dt o 0} v_t*d_t + x_t $$

我們拿到了計算新狀態的公式,但是 dt 是無限趨近于 0,怎么去模擬這個無限趨近于 0 呢?

現在只知道,當 dt 越趨近于 0 時,等式兩邊的值越接近(極限的單調有界準則可證)。可以把 dt 設為一個非常小的常量,雖然會造成一定的誤差,但是不足為慮,只要騙過人類的眼睛就可以了。

這樣我們就可以計算得出 v" 和 x" 。對以上過程不斷重復,就能計算出任意時刻的位移和速度。

這是個通用的模擬物理規律的緩動過程,是否讓你茅塞頓開?看一個同樣的模擬物理規律的動畫,有沒有手癢?

但是,原諒我又說了 “但是”,如果我們要用 raf 實現這個緩動的話,raf 不能設置 callback 的延遲時間,而我們的 dt 是一個固定的非常小的常量。這種情況下,怎么計算新的狀態呢?

我們設 raf callback 的延遲時間為 Δt ,第二部分已經說過,這個 Δt 是瀏覽器自己決定的。

不管 Δt 是多少,可以用幾個緩動過程連續疊加(一個緩動過程的時間是 dt )來拼湊出 Δt 。

不過 Δt 往往不是 dt 的整數倍,對于最后多出來的一小塊時間,我們可以取一個比例值。

const dt = 1000 / 60;

let preTime = 0
  , initialState = {
      currX: -250,
      currV: 0,
}

const update = () => {
  const currTime = performance.now();
  const deltaTime = currTime - preTime;
  const steps = deltaTime / dt;

  const multiObj = (obj, k) => {
    return Object.keys(obj).reduce((res, key) => {
      return { ...res, [key]: obj[key] * k }
    }, {})
  };

  const getCurrState = (prevState, steps) => {
    if (steps < 1) {
      return multiObj(cal(prevState), steps)
    }

    return getCurrState(cal(prevState), steps - 1)
  };

  render(getCurrState(initialState, steps))

  raf(update);
}

update()
動畫漫談

CSS 動畫與 JS 動畫的區別是,使用 CSS 動畫,不需要寫緩動過程。比如在 transition 中,可以使用現成的 cubic bizier 的緩動(其中 ease, ease-in, ease-out 等都是特定參數值的 cubic bizier)。

(值得一提的是,transition的實現也使用了 raf 的機制,當標簽頁被切換時, transition 動畫也會暫停,大家不妨試一試)

CSS 的 animation 使用設置關鍵幀的方式實現動畫,適合完成多步、往返或者不斷重復的動畫。

那么我們什么時候需要 JS 動畫呢——當你對 CSS 提供的緩動函數不滿意的時候。打個比方,如果想實現像淘寶網在加購成功后,讓商品 logo 沿著弧線運動的動畫。

React Motion 所做的事,只不過自己實現了一套緩動函數。如果你不關心緩動過程,用 CSS 動畫可以直接替換。

至于 React 當中的 ReactCSSTransitionGroup,是React提供的支持列表動畫的 API 。試想一下,當渲染函數發現新的列表狀態中,消失了某一項。那么要繪制這一項消失的動畫,必須先讓這一項暫存在 DOM 中,直到動畫結束,再從 DOM 消失。這個實現起來比較麻煩,所以 React 提供了這個 API 幫助我們實現動畫。值得注意的是,ReactCSSTransitionGroup 只是對列表的增與刪提供動畫支持。如果只是對列表項進行修改,不要生硬的套用 ReactCSSTransitionGroup,自己在 state 中管理列表實現起來更加方便。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/78388.html

相關文章

  • React Web 動畫的 5 種創建方式,每一種都不簡單

    摘要:以前一直投入在中,寫動畫的時候不是用中的,就是依賴像這樣的庫,最近轉向,在得到很多大佬關于動畫的回應,于是決定分享給大家,如有其他見解,非常歡迎在下面評論中交流以下便是本文要分享的創建動畫的幾種方式下面,勒次個特斯大特一特給元素添加是最簡單 以前一直投入在 React Native 中,寫動畫的時候不是用 CSS 中的 transitions / animations,就是依賴像 Gr...

    solocoder 評論0 收藏0
  • react進階漫談

    摘要:父組件向子組件之間非常常見,通過機制傳遞即可。我們應該聽說過高階函數,這種函數接受函數作為輸入,或者是輸出一個函數,比如以及等函數。在傳遞數據的時候,我們可以用進一步提高性能。 本文主要談自己在react學習的過程中總結出來的一些經驗和資源,內容邏輯參考了深入react技術棧一書以及網上的諸多資源,但也并非完全照抄,代碼基本都是自己實踐,主要為平時個人學習做一個總結和參考。 本文的關鍵...

    neuSnail 評論0 收藏0
  • react進階漫談

    摘要:父組件向子組件之間非常常見,通過機制傳遞即可。我們應該聽說過高階函數,這種函數接受函數作為輸入,或者是輸出一個函數,比如以及等函數。在傳遞數據的時候,我們可以用進一步提高性能。 本文主要談自己在react學習的過程中總結出來的一些經驗和資源,內容邏輯參考了深入react技術棧一書以及網上的諸多資源,但也并非完全照抄,代碼基本都是自己實踐,主要為平時個人學習做一個總結和參考。 本文的關鍵...

    wyk1184 評論0 收藏0
  • react進階漫談

    摘要:父組件向子組件之間非常常見,通過機制傳遞即可。我們應該聽說過高階函數,這種函數接受函數作為輸入,或者是輸出一個函數,比如以及等函數。在傳遞數據的時候,我們可以用進一步提高性能。 本文主要談自己在react學習的過程中總結出來的一些經驗和資源,內容邏輯參考了深入react技術棧一書以及網上的諸多資源,但也并非完全照抄,代碼基本都是自己實踐,主要為平時個人學習做一個總結和參考。 本文的關鍵...

    junnplus 評論0 收藏0

發表評論

0條評論

wfc_666

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<