同事拿到個設計稿,是旋轉木馬的輪播效果,因而網上找了一下,發現了jquery實現的效果,一有空就看看源碼,研究其實現原理,想着用vue的數據驅動方式來實現一個,但這個效果跟以往作的demo效果不一樣,每次思考實現都被卡住了。拖延了好些日子,後來腦子一轉,竟然想到方案了,因而動手實現了下。css
關於旋轉木馬效果實用例子:一個實際應用在優酷的頻道頁。html
首先分析一下jquery
的實現效果,實際上是將輪播項的dom進行左右分組,並設定好每一個dom的寬高、層疊參數、透明度以及位置偏移,而後經過jquery的動畫api,對dom組進行一個數據切換(複製下一個dom的數據)。好比如今有ABC三個,B在中間,當右往左切換時,B獲取C的數據,而C拿A的數據,而A替換B的數據,變成BCA.vue
插件接受指定的幾個數據:總體寬高,主項寬高(中間最大),其餘項依次縮小的比例。jquery
關於位置,如圖:git
主項應該是居中的,因此其位置偏移(絕對定位)左邊相對於父元素來講,應該是總體寬度和自身寬度之差的一半。而兩邊的其餘項,直接相隔則按數目等分剩餘空間。好比假設父元素寬度800px,主項寬度400px,左邊剩餘空間應該寬爲(800-400)/2=200px,而假設左邊有兩項,則兩者直接等分展現自身部分爲200/2=100px;這個數值就是咱們要考慮的絕對定位left值,同理可得右邊部分的left值,至於項目的居中絕對定位,也是相似的道理,更加簡單。github
其次,考慮的是以中間爲主項,往兩邊的項應該層疊參數(z-index)越靠中間,值越大,透明度越往兩邊值越小。具體代碼邏輯可從這裏找到。shell
vue的理念是數據驅動,因此要把jquery插件那套實現作一下轉化。一樣,也是接受幾個靜態數據:總體寬高、中間項寬高、縮小比例。api
<cascade-loop :list="list" :cur-width="400" :all-width="800" :all-height="300" :cur-height="280" :scale="0.8" ></cascade-loop>
將jquery對dom的數據操做轉化爲數組store
,數組元素存儲各項dom的數據(寬高、絕對定位偏移值、透明度以及層疊參數),在模板處遍歷實際項目的數組,其樣式則經過索引獲取對store
數組的元素數據。數組
切換數據經過對store
進行出隊、入隊或反向出隊和入隊操做以達到切換邏輯;再經過樣式設定transition
來設置動畫效果便可。dom
.item{ transition: all .8s ease; }
對於數據的初始化,代碼以下:
//store數組 var items = []; //拷貝物理數據 var rlist = copyArr(this.list); //獲取中間數組元素索引 var level = Math.floor(this.list.length/2); //若是數據是偶數則拷貝多一個數據. if(this.list.length%2==0){ var center = this.list[0]; rlist.push(Object.assign({},center)); } //左邊部分 var lefts = rlist.slice(0,level); //右邊部分 var rights = rlist.slice(level); var that = this; //兩邊剩餘空間(單邊) var leftGap = (this.allWidth - this.curWidth)/2; //等分剩餘空間,即間隙 var gap = leftGap/level; lefts.forEach(function(e,i){ //遍歷左邊部分 var obj = {}; //從左往右,其left值是gap倍數增加 obj.left = i*gap; //層疊參數逐級增1 obj.zIndex = i+1; //透明度則簡單作逐級增大 obj.opacity = 1/(level+1-i); //寬高則按距離中間項的個數來選擇縮放 obj.width = that.curWidth*Math.pow(that.scale,level-i); obj.height = that.curHeight*Math.pow(that.scale,level-i); //底部距離取最大高與當前項高差的一半 obj.bottom = (that.allHeight-obj.height)/2; items.push(obj); }); //遍歷右邊部分,包含中間項 rights.forEach(function(e,i){ var obj = {}; obj.width = that.curWidth*Math.pow(that.scale,i); obj.height = that.curHeight*Math.pow(that.scale,i); //偏移值能夠反向思考,經過全寬減去距離右邊偏移以及自身寬度 obj.left = that.allWidth - (level-i)*gap - obj.width; obj.zIndex = level-i+1; obj.opacity = 1/(i+1); obj.bottom = (that.allHeight-obj.height)/2; items.push(obj); }); return { items:items, rlist:rlist, timer:null, dir:'right' }
加上定時器和方向選擇,效果就出來了,以下:
https://jsfiddle.net/dont27/L...
訪問不了能夠看runjs.
剛開始一直思惟繞不過去,一直無從下手,然而當抓到突破點的時候,其實寫出來,發現挺簡單的!
不過在偶數項處理方面,我只是簡單作一下拷貝多一份的處理,比較粗暴!
PS: 有感興趣有想法的,歡迎留言評論,歡迎指教~
原文放在本身的博客上: http://shellphon.wang/githubl...