A rotatable component written with vue, as shown in the figure
-
layout
-
Use the number of cards to divide the 360 degree circle evenly, use the absolute positioning to distribute on the external container, and rotate itself through the rotate
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 } },
-
The external container is positioned under the browser window to expose the upper part
<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>
-
-
Rotation of the disk
- onmousedown and onmouseup are used to judge whether the mouse is in the down state and clear the last dragged data
- The rotation of the disc updates the angle with the total distance of lateral sliding
-
According to how the disk rotates, the design is to stack the overall sliding distance once at each small interval (such as 20ms)
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)); }
-
Smooth rotation
If there is no sliding inertia, after sliding, no matter how fast the speed is, the turntable stops immediately after releasing the mouse, making the effect very stiff. So after the completion of sliding, use the sliding speed at the last moment to make the rotary table move in uniform deceleration until the speed is 0, and when the speed is 0, in the design of slow and small uniform sliding, the final effect is relatively smooth. 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(){ let time = 10; let timer = setInterval(() => { this.xGap += this.direction*3; if(--time <= 0) clearInterval(timer); }, 20) },
- demo address: https://github.com/tanf1995/m...
-
Consult
The original idea is to pass the internal structure and array data of the card through prop, for example, to pass a rendering function, which can be easily realized through react, but vue is not feasible. How can we do this? The react pseudo code is as follows
<Component renderItem={item => <Child propName={item.props} data={item.data} />} > </Component>