從傳統動畫到react動畫過渡

從傳統動畫到react動畫過渡

之前放假在家的時候,群里有一個朋友問我,有沒有無縫輪播的思路,百度了一下,原來無縫輪播指的是傳統輪播圖中最後一張輪播圖下一張是第一張輪播圖,不會穿過中間的輪播圖。

給個例子吧

順便附上codepen地址

無縫輪播?

codepen.io圖標

傳統的思路

這是原生js實現的無縫輪播效果,給大家簡單講一下思路,以下一張為例

流程是: 假設六張圖片分別為 A B C D E F 點擊下一張button 所有圖片左移 A 圖片的寬度 圖片變為 B C D E F A

然後我們拿代碼來描述一下

var img0 = document.querySelector(img)[0]; var img0Width = img0.offsetWidth, var imgContainer = document.querySelector(.box); //裝載圖片的容器 imgContainer.animate({ marginLeft: -img0Width }, function(){ // 動畫結束回調方法 imgContainer.style.marginLeft = 0; //將marginleft 回復到0 var newNode = img0.cloneNode(true); // 替換順序 imgContainer.removeChild(img0); imgContainer.appendChild(img0); })

其實思路是很簡單的,但是仔細一想這裡有個不好的點,就是這句代碼。

imgContainer.style.marginLeft = 0;

為什麼說這句代碼不好呢?這裡是利用計算機強大的cpu,讓我們可以在肉眼不可見的速度迅速右移,我們來詳細模擬一下

下一張: 假設六張圖片分別為 A B C D E F 左移動畫(緩動) A B C D E F 動畫回調(瞬時) B C D E F A

大家有沒有發現這樣的動畫非常不符合規律

我們移動了裝載圖片的容器造成左移,但是最後又把它瞬間右移, 不符合圖片移動的語義化

react的思路

因為我們可以明確圖片的起始狀態

A B C D E => B C D E A 圖片的移動描述 (B C D E) 左移 (A) 右移

也就是說我們可以明確圖片的動畫前後狀態,而圖片的順序就是圖片的移動距離。這非常符合react的設計哲學啊,好,我們拿react再寫一版。

這裡我們用原生js來模擬一下react,方便大家加深一下react的學習。

  • 設置初始的state

const defaultState = Array(items.length).fill(0).map((item, index) => { return { key: `item${index}`, style: { left: (index + currentNum) * 100, opacity: 1 } } })

這裡items是img 標籤的nodelist,currentNum 是表示圖片初始的偏移量(-1 表示所有圖片左移1張,1表示所有圖片右移一張)

  • 順序改變後,更新state

const getState = (states, moveItemKey) => states.map((state, index) => { return { key: state.key, style: { left: (index + currentNum) * 100, opacity: moveItemKey === state.key ? 0 : 1 } } }) const setState = (newStates, moveItemKey) => { return getState(newStates, moveItemKey); }

這裡moveItemKey 是需要在 A B C D E => B C D E A 中 的 A 圖片。這裡有一個問題,當A圖片從第一張移動到最後一張時,A會穿過 B C D E, 這樣肯定是不行的,所以我們提前拿到了A的key,把A的透明度改變成了0,這樣即使穿過 B C D E,用戶也無法發覺。

  • 給 DOM 元素綁定key值

const setAttr = () => [...items].map((item, index) => { item.setAttribute(key, states[index][key]); }); const render = () => { [...items].map((item, index) => { var key = item.getAttribute(key); states.map((state, i) => { if (state.key === key) { item.style.left = state[style][left] + px; item.style.opacity = state[style][opacity]; } }); }) }

這裡我們把對元素的key值 綁在了dom的屬性上,render方法則會依據state的數據渲染dom。

  • 添加上一頁,下一頁事件

const prev = () => { var moveItem = states.slice(states.length - 1); newStates = [...moveItem, ...states.slice(0, states.length - 1)]; states = setState(newStates, moveItem[0].key); render(); } const next = () => { var moveItem = states.slice(0, 1); newStates = [...states.slice(1), ...moveItem]; states = setState(newStates, moveItem[0].key); render(); } prevButton.onclick = prev; nextButton.onclick = next;

以下一頁事件(next) 為例,我們只需要把圖片組中,第一張圖片移至最後就可以了。這裡有一點遺憾的是,沒實現state變化 自動render的部分,不過這裡主要是講react的動畫思路。

最後看看拿react思路改寫後的效果吧

順便附上codepen地址

無縫輪播-react?

codepen.io圖標

相比傳統的無縫輪播,拿rect的思路改寫後,代碼可讀性變的更高,思路更加清晰,同時代碼也變得更少,更容易維護。

官方的react-motion

當然最終的react動畫方案必須是react-motion了,motion提供了各種動畫參數,動畫做的異常逼真,自己也按照react-motion的API,實現了一版react-motion的無縫輪播,效果如下。

因為引入了react-motion,動畫成本大大降低,編寫的代碼也十分少。這裡留個地址吧,感興趣的同學可以去看看。

附上github地址

FounderIsShadowWalker/carousel?

github.com圖標

一點總結

現在來看動畫,覺得動畫都是漸進,有規律的,通過操作元素從一個位置瞬間移動到另一個位置,個人覺得是違背了動畫的理念的,因為從一個位置瞬時的改變到另一個點,這樣是無法描述(或者說不符合動畫的語義化),可以參照我們之前用傳統方法實現的輪播。

現在我們來寫動畫,我們應該先把動畫的狀態的描述出來,然後描述把動畫的起始態和結束態,這樣的動畫才是規律的。


推薦閱讀:

怎樣評價宮崎駿動漫《哈爾的移動城堡》?
騷中透著俏皮,噁心中帶點可愛,這就是四月霸權!
有個答案寫錯了(關於《你的名字》的版權)
《白雪公主》動畫片的創作背景是什麼?
《奇奇顆顆歷險記》第一部的片頭片尾曲叫什麼?

TAG:動畫 | React | 交互設計 |