demo地址: https://github.com/tanf1995/m...vue
用卡片數量均分360度圓,使用絕對定位分佈在外部容器上,自身經過rotate旋轉react
computedCardPosStyle(index){ let deg = index * this.unitCardDeg; let absDeg = Math.abs((deg + this.turnRotate) % 360); let z_index = absDeg > 180 ? Math.ceil(absDeg-180): Math.ceil(180-absDeg); return { width: this.cardWidth + "px", height: this.cardHeight + "px", top: -Math.cos(deg*Math.PI/180)*this.turntableR + "px", left: Math.sin(deg*Math.PI/180)*this.turntableR + "px", transform: `translate(-50%, -50%) rotate(${deg}deg)`, zIndex: z_index } },
外部容器定位於瀏覽器窗口之下,露出正上方部分出來git
<div class="container" :style="{ width: `${turntableR*2 + cardWidth}px`, height: `${turntableR*2 + cardHeight}px`, left: `${screenWidth/2 - turntableR-cardWidth/2}px`, top: `${ outerWrap? -(1-wrapScale)*(turntableR+cardHeight/2) + screenHeight - wrapScale*(cardHeight + bottomPos + cardHeight*reletiveTop): -(1-wrapScale)*(turntableR+cardHeight/2) }px`, transform: `scale(${wrapScale})`, }" ref="container" > ... </div>
針對圓盤如何轉動,設計按每個小的間隔時間(如20ms),疊加一次整體滑動的距離github
handleMouseDown(e){ e.preventDefault(); clearInterval(this.UDLMactionTimer); this.mouseIsDown = true; this.startX = e.clientX || e.touches[0].clientX; this.endX = e.clientX || e.touches[0].clientX; }, handleMouseUp(e){ e.preventDefault(); this.mouseIsDown = false; clearInterval(this.timer); clearInterval(this.UDLMactionTimer); this.timer = null; this.startX = 0; this.endX = 0; if(this.lastSpeed) this.UDLMaction(); }, handleMouseMove(e){ e.preventDefault(); this.endX = e.clientX || e.touches[0].clientX; if(!this.mouseIsDown) return; if(!this.timer){ this.timer = setInterval(() => { let moveGap = this.endX - this.startX; this.lastSpeed = moveGap/this.timeGap; this.xGap += moveGap; this.direction = moveGap > 0 ? 1 : -1; this.startX = this.endX; }, this.timeGap); } }, mounted(){ let container_dom = this.outerWrap ? this.$refs.outerWrap : this.$refs.container; container_dom.addEventListener('mousedown', this.handleMouseDown.bind(this)); container_dom.addEventListener('mouseup', this.handleMouseUp.bind(this)); container_dom.addEventListener('mouseleave', this.handleMouseUp.bind(this)); container_dom.addEventListener('mousemove', this.handleMouseMove.bind(this)); container_dom.addEventListener('touchstart', this.handleMouseDown.bind(this)); container_dom.addEventListener('touchend', this.handleMouseUp.bind(this)); container_dom.addEventListener('touchcancel', this.handleMouseUp.bind(this)); container_dom.addEventListener('touchmove', this.handleMouseMove.bind(this)); window.addEventListener('resize', this.responseContainerScale.bind(this)); window.addEventListener('load', this.responseContainerScale.bind(this)); }, beforeDestroy(){ let container_dom = this.outerWrap ? this.$refs.outerWrap : this.$refs.container; container_dom.removeEventListener('mousedown', this.handleMouseDown.bind(this)); container_dom.removeEventListener('mouseup', this.handleMouseUp.bind(this)); container_dom.removeEventListener('mouseleave', this.handleMouseUp.bind(this)); container_dom.removeEventListener('mousemove', this.handleMouseMove.bind(this)); container_dom.removeEventListener('touchstart', this.handleMouseDown.bind(this)); container_dom.removeEventListener('touchend', this.handleMouseUp.bind(this)); container_dom.removeEventListener('touchcancel', this.handleMouseUp.bind(this)); container_dom.removeEventListener('touchmove', this.handleMouseMove.bind(this)); window.removeEventListener('resize', this.responseContainerScale.bind(this)); }
若是沒有滑動慣性,當滑動完以後,不管滑動的時候速度如何的快,在鬆開鼠標後轉盤馬上停下,使得效果很是生硬。因此在滑動完成以後,利用最後時刻的滑動速度,讓轉盤作勻減速運動直至速度爲0,而且在速度爲0時,在設計緩慢細小的勻速滑動,最後呈現出來的效果就比較平滑了。數組
UDLMaction(){瀏覽器
let a = -this.reduceSpeed*this.direction; this.UDLMactionTimer = setInterval(() => { this.lastSpeed = (this.lastSpeed + a)*this.direction >= 0? this.lastSpeed + a: 0; this.xGap += (this.lastSpeed) * this.timeGap; if(!this.lastSpeed){ this.moreDynamic(); return clearInterval(this.UDLMactionTimer); } }, this.timeGap);
},
moreDynamic(){dom
let time = 10; let timer = setInterval(() => { this.xGap += this.direction*3; if(--time <= 0) clearInterval(timer); }, 20)
},函數
原本設想的是經過prop來傳遞卡片的內部結構和數組數據,例如傳遞一個渲染函數,經過react能夠輕鬆的實現,可是vue這招行不通。請問如何可以作到這點呢?react僞代碼以下佈局
<Component renderItem={item => <Child propName={item.props} data={item.data} />} > </Component>