以前放假在家的時候,羣裏有一個朋友問我,有沒有無縫輪播的思路,百度了一下,原來無縫輪播指的是傳統輪播圖中最後一張輪播圖下一張是第一張輪播圖,不會穿過中間的輪播圖。node
給個例子吧react
同時放上codepen的地址git
https://codepen.io/shadowwalkerzero/pen/XqeZjQgithub
這是原生js實現的無縫輪播效果,給你們簡單講一下思路,如下一張爲例app
流程是:
假設六張圖片分別爲 A B C D E F
點擊下一張button
全部圖片左移 A 圖片的寬度
圖片變爲 B C D E F A
複製代碼
而後咱們拿代碼來描述一下dom
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
複製代碼
你們有沒有發現這樣的動畫很是不符合規律spa
由於咱們能夠明確圖片的起始狀態設計
A B C D E => B C D E A
圖片的移動描述
(B C D E) 左移
(A) 右移
複製代碼
也就是說咱們能夠明確圖片的動畫先後狀態,而圖片的順序就是圖片的移動距離。這很是符合react的設計哲學啊,好,咱們拿react再寫一版。 這裏咱們用原生js來模擬一下react,方便你們加深一下react的學習。
1.設置初始的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表示全部圖片右移一張)
2.順序改變後,更新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,用戶也沒法發覺。
3.給 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。
4.添加上一頁,下一頁事件
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的地址
https://codepen.io/shadowwalkerzero/pen/rvGvjJ
相比傳統的無縫輪播,拿rect的思路改寫後,代碼可讀性變的更高,思路更加清晰,同時代碼也變得更少,更容易維護。
固然最終的react動畫方案必須是react-motion了,motion提供了各類動畫參數,動畫作的異常逼真,本身也按照react-motion的API,實現了一版react-motion的無縫輪播,效果以下。
由於引入了react-motion,動畫成本大大下降,編寫的代碼也十分少。這裏留個地址吧,感興趣的同窗能夠去看看。
如今來看動畫,以爲動畫都是漸進,有規律的,經過操做元素從一個位置瞬間移動到另外一個位置,我的以爲是違背了動畫的理念的,由於從一個位置瞬時的改變到另外一個點,這樣是沒法描述(或者說不符合動畫的語義化),能夠參照咱們以前用傳統方法實現的輪播。 如今咱們來寫動畫,咱們應該先把動畫的狀態的描述出來,而後描述把動畫的起始態和結束態,這樣的動畫纔是規律的。