起由
原始需求來源於一個項目的某個功能,要求實現3D圖片輪播效果,而已有的組件大多是普通的2D圖片輪播,於是重新造了一個輪子,實現了一個既支持2D,又支持3D的滑動、輪播組件。
實現思路
剛一開始肯定是無法直接實現3D滑動組件的,所以將功能拆分,如下步驟
- 實現一個雙向循環鏈表,作為底層Item的數據結構
- 基於鏈表,實現一個無限循環的2D滑動組件
- 基於2D滑動組件做3D變化得到3D組件
- 兼容性檢查以及功能封裝,簡化使用,確保穩定
雙向循環鏈表的模擬
以前看到的輪播組件大多是用數組來作為底層數據結構,最近,整合受簡書上的某一篇文章啟發,覺得鏈表方式挺不錯的,於是先動手造一個JS模擬的鏈表(一些現有的資源大多不符合預期,於是重新造輪)
原理
雙向循環鏈表原理比較簡單,這里不再贅述
實現
實現了一個雙向鏈表,支持循環和非循環,參考 https://github.com/dailc/jsDataStruct
2D滑動組件的實現
在H5時代,2D滑動組件大多是通過translate3d 和transition實現的,采用X軸位移來實現滑動,所以雖說最終的效果只是2D得平面滑動,其實也是基於3D變換的。
實現原理描述
假設屏幕寬為W,滑動組件中有(A,B,C,D,E )5個item,每一個item的寬度都占滿屏幕。采用循環模式(也就是E往右滑動可以到A)
- 構建雙向循環鏈表,其中從A開始
A._next = B;
B._next = C;
...
E._next = A;
A._prev = E;
B._prev = A;
...
E._prev = D;
如上,所以所有元素首尾相連,構成循環
-
初始化組件時,各自的位置如下(只考慮X軸(→),Y(↓)和Z(垂直屏幕)都是0)。
E(-2W),D(-W),A(0),B(W),C(2W)。
一般只會允許同時顯示最多5個元素,其它位置的元素暫時隱藏 -
通過監聽滑動組件的touch,來判斷當前組件應該的X軸唯一translateX
-
touchmove過程中,顯示的元素進行相應的translateX位移(向左滑為負,向右滑為正)。比如A的位置為(0+translateX),其它元素類似
-
touchend時,判斷當前的translateX,如果大於item寬度的50%,則判斷移到下一個位置,如果大於item寬度的150%,則移到下下個元素。同理,如果小於-50%,則移到到上各元素,小於-150%,則移動到上上個元素
-
移動到上一個元素,各自item的translateX 減去W即可,移動到下一個元素,加上W。並且移動過程中設置
transition(cubic-bezier(0.165, 0.84, 0.44, 1))
-
由於是通過雙向循環鏈表構建,所以每次移動只需要通過_next就可找到下一個元素,通過_prev即可找到上一個元素,並且自動循環
原理流程
以下為上述中向右滑動一位,的過程圖述
效果預覽
下圖是將item的寬度設為屏幕50%后的2D滑動效果
3D滑動組件的進一步實現
上述過程中實現了2D滑動組件,那么帶3D組件的組件該如何實現呢?畢竟最初的需求就是3D效果。
其實上述2D滑動組件就已經用到了3D變化了,只不過我們只用了X軸變換。所以看起來仍然是2D效果,所以接下來我們就是需要利用到另外兩個軸的變換。
最終效果圖示
最終我們需要實現如圖所示的3D滑動效果
基本3D坐標軸
我們將屏幕左上角看成原點。那么
- X軸就是平行於屏幕的水平方向,從左到右
- Y軸就是平行於屏幕的垂直方向,從上到下
- Z軸就是垂直於屏幕(垂直於這個面),由內向外(也就是從屏幕后面穿過屏幕,指向做電腦前面的人)
分析我們需要的3D變換
對比已經完成的2D效果和最終3D效果的圖,我們發現有以下區別
- Y軸存在旋轉(rotateY),目測,左側第一個Item的旋轉角度在(30度和45度之間)
- Z軸存在縱深(translateZ或translate3d),可以觀察出,屏幕中心的Item和左側或右側的縱深不一樣,屏幕中心的Z值更大(更突出)
- 需要找一個合適的視點(perspective),因為存在Z軸縱深,所以肯定是需要找到一個合適的視點來觀察整個組件的。
實際實現
實際實現過程中,和上述分析的過程一致,在2D變換的基礎上,針對Y軸進行了旋轉計算,針對Z軸進行了縱深計算。
基本過程是:
-
找到一個合適的Y軸旋轉計算公式(比如那左側的Item調試,找到一個合適的點)
-
找到合適的適應於不同屏幕分辨率的Z軸縱深以及視點位置,(比如在屏幕320時,找到一個合適的Z軸縱深和perspective,然后再屏幕768時再找到一個,進行線性插值即可)
詳細過程不再贅述
兼容性檢查以及功能封裝
初步做出來的效果並不能很好的兼容各種分辨率屏幕以及功能較為簡陋。
所以接下來的主要工作是:
- 找到合適的插值公式,使的3D變化適應於較多的屏幕。
- 檢查代碼,使的兼容更多的瀏覽器版本(比如Chrome,APP內嵌Webview,FireFox等等,當然了,肯定不考慮IE)
- 代碼封裝抽象工作,合理開放API,以及便於后續拓展(剝離第三方庫的依賴,進一步封裝后,使的類庫代碼更優雅)
基本上述工作完成后,一個嶄新的3D滑動組件類庫就完成了,它主要有如下功能;
- 支持3D滑動效果(核心功能)
- 兼容2D滑動效果(同時保證普通的使用)
- 開放以下API
* reset 重置,這個可以重寫更換options
* prev 上一個item
* next 下一個item
* moveTo 移動到某一個指定item,會尋找最短路徑
* bindItemChangedHandler 綁定item切換的監聽回調
* bindItemTapHandler 綁定item的tap監聽
* unBindItemTapHandler 解綁item的tap監聽,會取消所有的item的tap事件以及item內部的tap
* tap 綁定item內部某元素的tap事件,因為無法用click監聽,所以單獨提供了監聽函數
- 便於拓展,比如這個組件有提供一個圖片輪播拓展示例
源碼及效果
效果展示
源碼
原文地址
原文在我個人博客上面